4

OC基础补充及进阶_wx658264fcc0b64的技术博客_51CTO博客

 8 months ago
source link: https://blog.51cto.com/u_16456705/8924276
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

OC基础补充及进阶

精选 原创

钓鱼吊与叼 2023-12-21 15:02:14 博主文章分类:IOS ©著作权

文章标签 API 字符串 ios 文章分类 iOS 移动开发 yyds干货盘点 阅读数183

static

不能修饰属性,也不能修饰方法。但是可以修饰方法中的局部变量(下次再执行此方法时直接使用不会再声明)。

(类似于java中的类属性)

指向当前类或对象的指针(和java中的this类似)

Super

指向父类,想使用父类的方法时使用

@property

作用:自动生成getter和setter方法的声明

@property 数据类型 名称;
@property int     age;//去掉下划线.  _age

编译器在编译的时候会根据@property生成getter和setter方法的声明

可以批量声明(需类型一致)。

@synthesize

生成一个真私有属性与@property修饰的名字一样

自动生成getter、setter方法的实现(生成的setter方法没有逻辑判断,可以根据需求重写),将参数赋值给自动生成的私有属性

@synthesize 名称;

不生成私有属性的方法:

@synthesize @property名称 = 已经存在的属性名;
@synthesize age = _age;

可以批量声明@synthesize

@property增强

生成私有属性(私有的名称和property的名称一致, 属性的名称自动加下划线)

生成getter setter的声明

生成getter setter的实现(无逻辑验证)

可以批量声明相同类型属性,重写setter或getter任意一个,另一个都会自动生成,如果同时重写就不会生成私有属性。

父类的@property可以被子类继承,虽然@property生成的属性是私有的,在子类的内部无法直接访问,但是可以通过setter和getter来访问;

[super setter:XXXX];
  • 多线程相关
  • Atomic(默认值),会被加上线程安全锁
  •   特点:安全、效率低

  • Nonatomic不加锁
  • 与生成的setter方法的实现相关的参数
  • Assign(默认),生成的setter方法的实现就是直接赋值
  • Retain 生成的setter方法的实现是标准版的MRC。但不会自动的在dealloc中生成release代码,所以要手动添加。
  • - (void)setCar:(Car *) car{
        if(_car != car){
            [_car release];
            -car = [car retain];
        }
    }

属性类型是OC对象时用retain,非OC对象时,assign

  • 与生成只读、读写相关的参数
  • Readonly 只会生成getter
  • Readwrite(默认值)生成getter setter
  • 与生成的setter getter方法名字相关的参数
  • Getter修改getter的名字
  • setter修改setter的名字
  • 与生成的属性类型强弱有关的参数
  • Strong
  • 无论是MRC还是ARC,只要类型是NSString,都用copy

访问修饰符(只能修饰属性,不能修饰方法)

@private 只能在本类内部或本类的方法实现中访问

@protected 只能在本类及其子类中访问(默认等级)

@package 可以在当前框架中访问

@public 任意地方都可以访问

使用@private修饰的属性 虽然是私有属性,但是依然会被提示,只是没有权限

如果声明属性在类的实现中那么将不会被提示,可以做到真正的私有

只能在本类中的其它方法调用(只写实现不写声明)

Setter方法与getter方法

set+变量名 提供一个方法给外界,设置变量的值; get+变量名 返回对象变量的值;set方法后跟的变量名首字母必须大写。

-(void)setName:(NSString * )name;//setter
-(NSString *)name;//getter

根据具体使用场景来进行封装,如只读封装和只写封装等

如果方法的返回值是当前类,关键字用instancetype

description

使用%@输出创建的对象时,会先调用传入的对象的description方法,返回一个NSString字符串,然后将这个字符串输出在控制台。

默认方法实现 @"<Class:对象地址>";

可以通过重写来自定义输出格式

里氏替换原则

子类可以替换父类,并且不影响原有功能

同一行为,对于不同的事物(子-父类)具有完全不同的表现形式,可以对父类的方法进行重写来实现自己独有的功能。

不使用指针接收对象的地址,每次创建的对象都是不同的;

[MyObject new];

使用场景:对象只用一次

得到类在代码段中的地址方式

  1. 调试查看对象的isa指针的值
  2. 在类方法中查看self的值
  3. 调用对象或者类的class方法

OC中类之间的关系

1个对象是由多个对象组合起来的.

比如.计算机对象. 是由主板对象、CPU对象、内存对象、硬盘对象...组合起来的.

主板、内存、硬盘作为计算机对象的属性.

那么这个时候,计算机对象和主板、内存、硬盘的关系为 组合关系.

1个对象的方法的参数是另外1个对象.那么我们就说他们的关系是依赖关系.

比如,B类是A类方法的参数,我们就说A类依赖于B类.

人打电话的例子.

callWithPhone:(Phone *)phone;

我们就说人类依赖于电话类. 人要打电话 就必须要有电话对象.

耦合度: 当修改1个对象的时候 对另外1个对象的影响程度.

A类和B类. 如果修改了B类. 发现A类就无法使用了,我们就说他们的耦合度很高.

低耦合: 当修改1个对象的时候 对另外1个对象的影响较小甚至没有影响.

高内聚: 1个对象仅仅做自己相关的事情. 跟自己无关的事情就不要写在类中.不要写1个大杂烩.

单一职责原则:1个类只做自己的事情.别人的事情给别人做.

关联体现的是两个类之间语义级别的一种强依赖关系,

比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性 的,

而且双方的关系一般是平等的。关联可以是单向、双向的。

表现在代码层面,

为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类 型为被关联类B的全局变量。

面向对象编程(OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

OC 的类是单继承,一个类只能有一个父类

OC中所有类的源头是NSObject

Objective-C的异常比较像Java的异常处理,也有@finally的处理,不管异常是否捕获都都要执行。

异常处理捕获的语法:

@try {  
      <#statements#>  
  }  
  @catch (NSException *exception) {  
      <#handler#>  
  }  
  @finally {  
      <#statements#>  
  }

@catch{} 块 对异常的捕获应该先细后粗,即是说先捕获特定的异常,再使用一些泛些的异常类型。

内存中的五大区域

栈:局部变量,当局部变量的作用域被执行完毕后。立马被系统回收

堆:OC对象,使用C函数申请的空间(系统不会自动回收,直至程序结束)

BSS段:未初始化的全局变量、静态变量,一旦初始化就回收,并转存到数据段中

数据段:已经初始化的全局变量、静态变量。程序结束才会回收

代码段:代码,程序结束的时候,系统自动回收存在代码段的数据

需要自己手动管理的只有堆。

引用计数器

  1. 每一个对象都有一个属性,叫做retainCount。类型是unsigned long8个字节,用来记录有多少个对象引用它。(默认情况:创建一个对象出来就是1)
  2. 当retainCount为0时,代表这个对象无人可用,这时系统自动回收(调用dealloc方法)
  1. 为对象发送一条retain消息,retainCount+1
  2. 发送release,-1
  3. 发送retainCount,可以取到计数器的值

MRC和ARC

MRC-手动引用计数器(使用时要关闭ARC)

重写dealloc规范:必须调用父类的dealloc方法,并且要放到最后一行

有创建就要有对应的release

一个retain和一个release对应

setter规范

- (void)setCar:(Car *) car{
    if(_car != car){
        [_car release];
        -car = [car retain];
    }
}

dealloc规范

-(void)dealloc{
    [_car releadse];
    [super dealloc];
}

自动释放池

  • 原理:存到自动释放池中的对象,在自动释放池被销毁的时候,会自动调用存储在该自动释放池中的所有对象的release方法(只是发送一次)
  • 创建和使用:
  • @autoreleasepool {
        Person * p = [[[Person alloc] init] autorelease];
    }

只有里面调用autorelease才能自动释放(同一个对象调用几次,发送几次release)

在集合中存储数据时,引用计数自动加一,当集合销毁时,会自动减一

copy深浅拷贝
  • 本身NSString自带的copy是浅copy(没有创建新对象)
  • NSMutableString的copy是深copy,但产生的字符串对象不可变(创建了一个新对象)
  • mutableCopy定义在NSObject中,是深copy并且字符串对象可变

常量区的字符串不会被回收,其引用计数器是一个超大的数,并且retain和release无效

堆区的字符串和普通对象一样,且引用计数器默认是1

ARC-自动引用计数器

系统自动retain、release、autorelease、dealloc

本质:当引用计数器为0时,自动释放

表象:只要没有强指针指向这个对象,这个对象就会立即回收

可以通过@property中的weak和strong来标识或者__strong __weak

默认情况下,所有指针都是强指针

ARC中当对象被回收时,弱指针的值被设为nil

ARC中的内存泄漏

和下面的循环引用同理。

解决:一个使用strong一个使用weak

ARC与MRC的兼容

在编译设置->Build Phasses->Complie Sources中,选择要修改的类,在flags中写入 -fno-objc-arc即可让此类不用ARC

MRC转化为ARC

点击菜单里的edit->convert->ARC 即可(慎用,因为只是简单的删除,可能会出错)

ARC与垃圾回收器的区别

  • GC:程序在运行期间,存在垃圾回收器,不断扫描堆中的对象是否 无人使用
  • ARC:编译时在合适的地方插入retain,无引用时回收

ARC模式下集合的元素是一个强类型的指针

一个对象没有被及时回收,一直留在内存里,直到程序结束才回收。

单个对象的内存泄漏

  1. 有对象创建物release
  2. retain和release数量不匹配
  3. 在不适当的时候给指针设为nil
  4. 在方法中为传入的对象不适当的retain

C:定义一个指针变量,没有初始化。变量的值是一个垃圾值,随机指向一块空间

OC:指针的对象已经被回收了

一个已经被释放的对象,但是这块空间还未被分配给别人

通过野指针访问此对象时,有可能有问题(空间被分配给别人),也可能没有问题(空间还没有被分配)

存在一个僵尸对象的实时检查机制,打开后,只要访问的是僵尸对象,无论空间是否被分配,都会报错(非常消耗性能

避免僵尸对象错误

当指针成为野指针后,将值设为nil(nil调用方法不会报错,访问属性会报错)

@class

当多文件开发时,两个类互相包含。如Person.h中包含Book.h,而Book.h中包含Person.h。这个时候就会出现循环引用问题,造成无限递归,导致编译无法通过

解决方案:

不使用#import,使用@class类名来标注这是一个类,在.m文件中在#import对方的头文件就可以使用了

与#import区别

  1. #import是将指定的文件的内容拷贝到写指令的地方
  2. @class并不会考呗任何内容,只是告诉编译器,这是一个类。

A、B互为对象,两边都使用retain,就会发生内存泄漏

解决方法:

一端使用retain,另一端使用assign,在使用assign的那一端在dealloc中不再需要release

字符串读写

  • 将字符串内容写入到磁盘上的某个文件中
- (BOOL)writeToFile:(NSString *)path 
    //文件路径
    atomically:(BOOL)useAuxiliaryFile 
    //YES 先将内容写到临时文件里,成功后再写到指定目录,安全,效率低
    //NO 直接将内容写到指定文件, 不安全,效率高
    encoding:(NSStringEncoding)enc 
    //指定写入使用的编码一般UTF8,NSUTF8StringEncoding
    error:(NSError **)error;
    //二级指针,传入NSError指针的地址
    
  返回值为BOLL类型,返回是否成功写入
  • 从指定的文件中读取字符串
+ (nullable instancetype)stringWithContentsOfFile:(NSString *)path 
encoding:(NSStringEncoding)enc 
error:(NSError **)error;
  • 使用URL来读取字符串数据
  • 优势:既可以读本地磁盘文件,又可以读网页文件、发FTP服务器上的文件
  • 不同类型的URL地址写法不同
  • 本地磁盘文件:file:///Users/admin/Desktop/XXX.xxx
  • 网页地址;http://gz.xxxx.cn
  • FTP文件:ftp://server.xxx.cn/xx/xxx.xx
  • 将不同类型的地址封装在NSURL对象中
  • NSURL *u1 = [NSURL URLWithString:@"https://www.bilibili.com/video/BV1NJ411T78u?p=167&spm_id_from=pageDriver&vd_source=4852f0fd2838c9dc71dd11e6d14e8735"];
    NSError *err;
    NSString *str = [NSString stringWithContentsOfURL:u1 encoding:NSUTF8StringEncoding error:&err];
        
    NSLog(@"%@", str);
NSURL *u1 = [NSURL URLWithString:@"file:///Users/admin/Desktop/abc.txt"];
NSString *str = @"hello";
[str compare:str options: 1];
NSError *err;
BOOL res = [str writeToURL:u1 atomically:NO encoding:NSUTF8StringEncoding error:&err];
if(res == YES){
    NSLog(@"写入成功");
}else{
    NSLog(@"失败");
    NSLog(@"%@",err.localizedFailureReason);
}
  • 保存到磁盘
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile A
PI_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically 
API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
  • 从磁盘读取
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path API_DEPRECATED_WITH_REPLACEMENT("arrayWithContentsOfURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfURL:(NSURL *)url API_DEPRECATED_WITH_REPLACEMENT("arrayWithContentsOfURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED));
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically API_DEPRECATED_WITH_REPLACEMENT("writeToURL:error:", macos(10.0, API_TO_BE_DEPRECATED), ios(2.0, API_TO_BE_DEPRECATED), watchos(2.0, API_TO_BE_DEPRECATED), tvos(9.0, API_TO_BE_DEPRECATED)); // the atomically flag is ignored if url of a type that cannot be written atomically.
  • 从磁盘取出
NSMutableDictionary *newdict = [NSMutableDictionary dictionaryWithContentsOfFile:@"/Users/admin/Desktop/dict.plist"];



+ (nullable NSMutableDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;
+ (nullable NSMutableDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfURL:(NSURL *)url;
- (nullable NSMutableDictionary<KeyType, ObjectType> *)initWithContentsOfFile:(NSString *)path;
- (nullable NSMutableDictionary<KeyType, ObjectType> *)initWithContentsOfURL:(NSURL *)url;

NSFileManager

管理文件的一个类

  • 判断指定文件或文件夹是否存在
- (BOOL)fileExistsAtPath:(NSString *)path;
  • 判断路径是否存在并且判断是文件路径还是文件夹路径
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(nullable BOOL *)isDirectory;
YES 是文件夹
NO
  • 判断指定的文件夹或文件是否可以读取(有些文件权限不够不能读取)
- (BOOL)isReadableFileAtPath:(NSString *)path;
  • 判断指定的是否可以写入
- (BOOL)isWritableFileAtPath:(NSString *)path;
  • 判断指定文件是否可以删除
- (BOOL)isDeletableFileAtPath:(NSString *)path;

文件获取信息

  • 获取属性信息(要拿到特定的信息通过Key
- (nullable NSDictionary<NSFileAttributeKey, id> *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
  • 获取指定目录下的所有的文件和目录,子目录的子目录也可获得
- (nullable NSArray<NSString *> *)subpathsAtPath:(NSString *)path;
  • 只拿到子目录
- (nullable NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

文件/目录 创建

  • 指定路径创建文件
- (BOOL)createFileAtPath:(NSString *)path    路径
contents:(nullable NSData *)data        文件的二进制数据
attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attr;   指定创建文件的属性,默认为nil


实例:
NSString *str = @"lalalala";
NSDate *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[manager createFileAtPath:@"/Users/admin/Desktop/la.txt" contents:data attributes:nil];
  • 指定路径创建文件夹
- (BOOL)createDirectoryAtPath:(NSString *)path w
ithIntermediateDirectories:(BOOL)createIntermediates 
YES 做一路创建(没有上级,可以直接创建)  NO 不会一路创建(没有上级,不会创建)
attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attributes 
error:(NSError **)error 
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (BOOL)copyItemAtPath:(NSString *)srcPath 
toPath:(NSString *)dstPath 
error:(NSError **)error 
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

移动文件(剪切,重命名)

- (BOOL)moveItemAtPath:(NSString *)srcPath 
toPath:(NSString *)dstPath 
error:(NSError **)error 
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

不会删到废纸篓,而是直接删除,谨慎使用

- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error 
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

NSFileHandle

NSFileHandle类允许更有效的使用文件,可以实现如下功能:

  • 对文件,执行读、写或更新读写操作;
  • 在文件中查找指定位置;
  • 从文件中读取特定数目的字节,或将特定数目的字节写入文件中

另外,NSFileHandle类提供的方法也可以用于各种设备或套接字。一般而言,我们处理文件时都要经历三个步骤:打开文件,获取一个NSFileHandle对象;对打开文件执行相关操作;关闭文件。

一般流程(下面还有实例)

一、只读读取文件内容
//NSFileHandle对文件内容进行操作
//获取沙盒中某txt文件的路径
NSString *homePath = NSHomeDirectory();
NSString *path = [homePath stringByAppendingPathComponent:@"Documents/file.txt"];
//以只读的方式打开文件生成文件句柄
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
//注:内存:内部存储器;硬盘:外部存储设备。从硬盘到内存(从文件到内容)叫做读,从内存到文件(硬盘)叫做写
//读取文件内容的两种方式
//    NSData *data = [fileHandle readDataOfLength:3];
//    data = [fileHandle readDataOfLength:5]; //继续上面3个字节后,继续读取5个字节
NSData *   data = [fileHandle readDataToEndOfFile];//如果文件内容不是特别多,可以直接读取全部内容
二、只写修改文件内容
//NSFileHandle对文件内容进行操作
//获取沙盒中某txt文件的路径
NSString *homePath = NSHomeDirectory();
NSString *path = [homePath stringByAppendingPathComponent:@"Documents/file.txt"];
//以只写方式打开文件生成句柄
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path];
[handle writeData:[@"Hello world!!!" dataUsingEncoding:NSUTF8StringEncoding]];//直接覆盖掉前面相应数量的字符
[handle truncateFileAtOffset:0];//将文件字节截短至0,相当于将文件清空,可供文件填写
[handle writeData:[@"Hello world." dataUsingEncoding:NSUTF8StringEncoding]];//填写文件
[handle seekToEndOfFile];//将读写指针设在文件的尾端
[handle writeData:[@"ni hao" dataUsingEncoding:NSUTF8StringEncoding]];
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"---%@",str);
+(NSFileHandle*)fileHandleForReadingAtPath:path 打开一个文件用于读入

+(NSFileHandle*)fileHandleForWritingAtPath:path 打开一个文件用于写入

+(NSFileHandle*)fileHandleForUpdatingAtPath:path 打开一个文件用于读写

-(NSData*)availableData 从设备或者通道返回可用数据

-(NSData*)readDataToEndOfFile 读取其余的数据知道文件的末尾(最多UINT_MAX字节)-(NSData*)readDataOfLength:(NSUInteger)bytes 从文件中读取指定字节的内容

-(void)writeData:data  将data写入文件

-(unsigned long long)offsetInFile 获取当前偏移量

-(void)seekToFileOffset:offset  设置偏移量

-(unsigned long long)seekToEndOfFile 将偏移量定位到文件的末尾

-(void)truncateFileAtOffset:offset 讲文件的长度设置为offset字节

-(void)closeFile 关闭文件

方法fileHandleForWritingAtPath和fileHandleForUpdatingAtPath所指定的文件必须是已经存在的,否则返回nil,另外对于这两个方法中文件的偏移量都是为文件的开始。

方法readDataToEndOfFile每次从文件中读取最多UNIT_MAX字节的数据,这个量定义在
<limits.h>中。
#import <Foundation/Foundation.h>  

int main(int argc, const charchar * argv[])  
{  

    @autoreleasepool {  
        NSFileHandle *inFile,*outFile;  
        NSData *buffer;  
        NSString *fileContent = @"这些是文件内容,这些是文件内容,这些是文件内容,这些是文件内容,这些是文件内容";  
        NSFileManager *fm = [NSFileManager defaultManager];  

        //创建一个文件  
        [fm createFileAtPath:@"testFile.txt" contents:[fileContent dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];  
        //创建一个需要写入的文件  
        [fm createFileAtPath:@"outFile.txt" contents:nil attributes:nil];  

        //读取文件  
        inFile = [NSFileHandle fileHandleForReadingAtPath:@"testFile.txt"];  
        //写入文件  
        outFile = [NSFileHandle fileHandleForWritingAtPath:@"outFile.txt"];  

        if(inFile!=nil){  
            //读取文件内容  
            buffer = [inFile readDataToEndOfFile];  

            //将文件的字节设置为0,因为他可能包含数据  
            [outFile truncateFileAtOffset:0];  

            //将读取的内容内容写到outFile.txt中  
            [outFile writeData:buffer];  

            //关闭输出  
            [outFile closeFile];  

            //验证outFile内容  
            NSLog(@"%@",[NSString stringWithContentsOfFile:@"outFile.txt" encoding:NSUTF8StringEncoding error:NULL]);  

            //创建一个新的文件用来循环写入  
            [fm createFileAtPath:@"outFile2.txt" contents:nil attributes:nil];  

            //打开一个新的输出  
            outFile = [NSFileHandle fileHandleForWritingAtPath:@"outFile2.txt"];  

            //设置一个循环写入10条数据,每条数据都再后面添加上而不是覆盖  
            for (int i = 0; i<10; i++) {  
                //将偏移量设置为文件的末尾  
                [outFile seekToEndOfFile];  
                //写入数据  
                [outFile writeData:buffer];  
            }  

            //验证内容  
            NSLog(@"outFile2:%@",[NSString stringWithContentsOfFile:@"outFile2.txt" encoding:NSUTF8StringEncoding error:NULL]);  

            //关闭所有  
            [outFile closeFile];  
            [inFile closeFile];  

        }  

    }  
    return 0;  
}

文件存储-补充:

 文件存储-补充

OC中的特性语法

Class

有基本的三个属性:类名、属性S、方法S

类是以class对象的形式存储在代码段中的,class中也有isa指针,这个指针指向存储父类的类对象

可以使用class调用类的类方法

拿到这个类对象

  调用类的类方法class

  调用对象的对象方法class

  对象中的isa指针的值就是代码段中存储类的类对象地址

  声明class指针的时候不需要加*,在typedef的时候已经加过

SEL就是selector选择器

SEL是一个数据类型(class的属性)(可以理解为类),因此要申请分配空间,用来存储方法的

拿到存储方法的SEL对象

SEL s = @selector(方法名)

SEL是typedef的类型,在自定义时加过*了

调用方法的本质-消息机制

[object init];
  1. 先拿到存储init方法的对象,也就是拿到存储init方法的SEL数据。SEL消息
  2. 将这个消息发送给object对象
  3. object对象接收到这个SEL消息后,就知道要调用方法
  4. 根据对象的isa指针找到存储类的类对象
  5. 找到这个类对象以后 在这个类对象中搜寻是否有和SEL数据相匹配的。如果没有就再找父类

手动发送SEL消息

- (id)performSelector:(SEL)aSelector;

无参方法
Person *p = [Person new];
SEL s = @selector(say);
[p performSelector:s];//与[p say]效果一样

有参方法
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

多个参数
将参数封装起来再调用- (id)performSelector:(SEL)aSelector withObject:(id)object;

当类的属性有getter和setter时

person.name = @"10";//原理,调用setter方法。getter同理
等价于
[person setName:10];

在getter和setter中慎用点语法,可能造成无限递归

- (void)setAge:(int)age{
    self.age = age;//等价于[self setAge:age]
}

OC是弱语言,编译器编译时语法检查不严格

int num = 12.2;

优点:灵活

缺点:太灵活

静态类型和动态类型

静态:指针指向本类对象

Animal *a = [Animal new];

动态:指针指向非本类对象

Animal *a = [Pig new];
Animal *a = @"animal";//用NSLog输出后是animal

NSObject

是一个万能指针,可以指向任意对象;

缺点:调用子类的独有方法时需要类型转换。

是Foundation中的一个类,是所有类的基类

万能指针,可以指向任意对象

id是个typedef自定义类型,在定义时已经加了*

id和NSObject的异同

相同点:万能指针, 都可以执行任意OC对象

不同点:通过NSObject指针调用对象的方法时,编译器会做编译检查

  id指针调用时,直接通过检查。

id指针只能调用对象的方法,不能使用点语法

动态类型检测

判断当前对象中是否存在某一方法,避免调用不存在方法导致报错

- (BOOL)respondsToSelector:(SEL)aSelector;

判断当前类是否存在改类方法

+ (BOOL)instancesRespondToSelector:(SEL)aSelector;

判断指定的类是否为某类或其的子类

- (BOOL)isKindOfClass:(Class)aClass;

判断是否是该类,不包括子类

- (BOOL)isMemberOfClass:(Class)aClass;

init-初始化方法

给对象赋一个初值(0,null,nil)

重写init

  1. 必须要先调用父类的init方法,然后将方法的返回值赋给self
  2. 调用init方法有可能会失败,如果失败返回的就是nil
  3. 判断父类是否初始化成功。判断self的值是否为nil,不为nil说明成功
  4. 如果初始化成功,就初始化当前对象
  5. 最后返回self的值
-(instancetype)init{
    self = [super init];
    if(self){
        //初始化
    }
    return self;
}

init的返回值必须是instancetype,名字必须以initWith开头,方法的实现和init的要求一样。

category-类别、分类

将一个类分成多个模块,这样方便维护和管理

  • 建立一个新文件,类型选择category,选择要要分类的类
  • 会生成一个.h 和.m文件,文件名为:类名+分类名
  • 添加的分类也分为声明和实现
  • @interface 本类名 (分类名)
    @end
    
    @implementation 本类名 (分类名)
    @end
  • 需要把分类的头文件引进来
  • 分类只能增加方法,不能增加属性
  • 在分类中可以写@property但是不会自动生成私有属性,也不会生成getter和setter的实现。只会生成getter和setter的声明(不能生成可以手写)
  • 在分类的方法实现中不可以访问本类的真私有属性(@property生成的),但是可以调用本类的getter和setter来访问
  • 当分类中有和本类中同名的方法时,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后的分类
  • 把一个类分成多个模块
  • 为一个已经存在的类添加方法

延展- Extension

  • 是一个特殊的分类,是类的一部分
  • 特点:没有名字、只有声明没有实现,与本类共享一个实现
@interface NSObject ()
@end

与分类的区别

  1. 分类有名字,延展没有,是一个匿名的分类
  2. 每个分类都有单独的声明和实现,而延展只有声明没有单独实现,和本类共享一个实现
  3. 分类中只能新增方法,而延展中任意的成员都可以写
  4. 分类中可以写@property,但是只会生成getter和setter的声明,延展中写@property会自动生成私有属性,也会生成getter和setter的声明和实现
  1. 为类写一个私有的@property,相当于只生成了私有属性,没有声明setter和getter,实现了setter和getter
  2. 延展不会单独占一个文件,都是直接写在本类的实现文件中,相当于这个类的私有成员,只能在本类的实现中访问,外部不能访问
  3. 想为类定义真私有属性,方法(方便查看有哪些私有方法),@property时,使用

Block

block类型的变量中专门存储一段代码,这段代码中可以有参数,可以有返回值(类似于函数指针和lambda表达式)

void (^myblock) () = ^void (){
    NSLog(@"block");
};
   
void (^myblock) (int num) = ^void (int num){
    NSLog(@"block=%d", num);
};

int (^myblock) (int num) = ^int (int num){
    NSLog(@"block=%d", num);
    return num;
};
  • 代码段的返回值时void可以省略,声明不能
void (^myblock) () = ^ (){
    NSLog(@"block");
};
  • 如果没有参数,代码段的括号可以省略
void (^myblock) () = ^{
    NSLog(@"block");
};
  • 声明block时,如果有指定参数,可以只写参数的类型,不写名称
int (^myblock) (int) = ^int (int num){
    NSLog(@"block=%d", num);
    return num;
};
  • 可以省略代码段的返回值类型
NSString* (^myblock2) (NSString *) = ^(NSString* num){
    NSLog(@"block2=%@", num);
    return num;
};

block内部访问外部变量

  • 在内部可以取定义在外部的变量值(局部、全局)
  • 内部可以修改全局变量的值,但是不能直接修改定义在外部的局部变量值
  • 如果一个外部的局部变量参数想让block内部修改,要加一个__block

block作为参数

typedef void (^NewType)();

void test(NewType block){//. void test(^(void)(){})
    block();
}


NewType type = ^{};
test(type);
test(^{});

作为返回值

  • 作为返回值时要使用typedef定义的短类型

block与函数

  • 相同点:都是封装一段代码
  • block是一个数据类型,函数就是函数
  • 可以声明block的变量
  • block可以作为函数的参数,函数不能
  • 专门用来声明一大堆方法(不能声明属性,也不能实现方法,只能写方法的声明)
  • 只要某个类遵守了这个协议,就相当于拥有了这个协议中的所有方法的声明,不用自己去定义
@protocol MyProtocol <NSObject>

-(void) run: (NSString *)str;
@end
@interface Player : NSObject <Prot>{//类名:父类名 <协议名>
    @private
    NSString *_name;
    int _score;
    Type _selectType;
}

类是单继承的,但是协议可以多遵守(<XXX, XXX, XX>)

@required和@optional

  • @required,被此修饰的方法,必须实现,不实现就爆警告
  • @optional,不实现不回警告

不实现都不会报错

协议的继承

协议可以被继承,并且是可以多继承

@protocol MyProtocol <NSObject,XXX,XXX>
@end

NSObject

Foundation中还存在一个NSObject协议,NSObject协议被NSObject类遵守,所以NSObject协议中的所有方法,全部的OC类都拥有。NSObject协议叫做基协议。(协议名可以和类名重复)

协议类型限制

如果该对象没有实现该协议,会报警告

id<MyProtocol> obj = [Player new];
NSObject<MyProtocol> *obj = [Player new];
id<MyProtocol, YourProtocol> obj = [Player new];

非正式协议

为系统写的分类就是非正式协议

特性语法-补充

 特性语法-补充

关于Xcode

查看文档:点击window找到documentation可以查看文档

右侧边栏中点击问号,然后在点击代码即可看到相关解释

OC基础补充及进阶_ios


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK