0
点赞
收藏
分享

微信扫一扫

OC基础补充及进阶

关键字

static

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

(类似于java中的类属性)

Self

指向当前类或对象的指针(和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
  • Weak
  • 无论是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

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指针

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

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


举报

相关推荐

0 条评论