OC 程序的源文件的后缀名是 .m。 m 代表 message。 代表 OC 中最重要的 1 个机制 消息机制
#import 是 #include 的增强版,将文件和内容在预编译的时候拷贝到写指令的地方。增强点:同一个文件无论 #Import 多少次,只会包含一次。#include 只能通过条件编译指令来实现
Foundation 框架,提供了一些最基础的功能(输入输出、一些数据类型、NSLog 等)
@autoreleasepool 自动释放池。
NSLog 是 printf 的增强版,会输出一些调试相关信息、自动换行
1
22022-07-09 10:43:47.115631+0800 basic_command_line[82785:1924379] Hello, World!
[82785:1924379]: 进程pid 和 线程 tidNS 前缀。NextStep 公司来的,历史原因
OC 中的绝大部分的关键字都是以 @ 符号开头
注释,单行注释和多行注释,和 C 语言一样
编译链接
1
2
3
41. 将源代码编译为目标文件。预处理、检查语法、编译
cc -c xx.m -o main.o
2. 链接。注意程序中用到那个框架的功能,需要链接
cc main.o -o main -framework FoundationOC 的数据类型,支持 C 语言所有数据类型
1
2
3
4
5
6
7
8BOOL 类型,可以存储 YES、NO
Boolean 类型,可以存储 true 或者 false
BOOL 和 Boolean 在底层都是 unsigned char。YES 和 NO 底层就是数字 1 和 0
class 类型,类
id 类型,万能指针
nil 和 NULL 差不多
SEL 方法选择器
block 代码段类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19类的声明
@interface 类名 : NSObject
{
@public
NSString* _name; // 定义在大括号之中用来表示某类事物的共同特征的变量叫做属性(成员变量)
}
@end
类的实现
@implementation 类名
@end
类名首字母必须大写
为类定义属性时,属性的名词必须要以下划线 _ 开头,这时一个规划。
创建类的对象 class_name* p1 = [class_name new];
默认情况下,对象的属性不允许访问。可以加一个 @public
访问属性。对象名->属性 或 (*对象名).属性
类方法:- (返回值)方法名:(参数类型)参数名 :(参数类型)参数名 :(参数类型)参数名;
规范:如果方法只有1个参数,要求最好这个方法的名字叫做 xxxWithxx,提高可读性创建对象,new 关键字。在堆内存中申请一块合适大小的空间,在这个空间中根据类的模板创建对象。类模板中定义了什么属性,就把这些属性依次的声明在对象之中。对象中还有另外一个属性,叫做 isa 指针,指向对象所属的类在代码段中的地址。
- 对象中只有属性,而没有方法。自己类的属性外加 isa 指针指向代码段中的类
- 如何调用方法,先根据指针名找到对象,对象发现要调用方法。再根据对象的 isa 指针找到类。然后再调用类中的方法。
对象属性的默认值。new 对象的时候,如果不赋值,就是赋默认值
- 如果属性的类型是基本数据类型,默认值是 0
- 如果属性的类型是 C 指针类型,默认值是 NULL
- 如果属性的类型是 OC 指针类型,默认值是 nil
nil 和 NULL。NULL 就是一个宏,就是 0。nil 只能作为指针变量的值,也是一个 (void*)0
C 指针用 NULL,OC 的类指针用 nil
如果一个类指针的值为 nil,代表这个指针不指向任何对象。ClassName* p = nil。此时,用 p 指针访问对象属性,会运行报错;但是调用对象的方法,运行不会报错,但是方法不会执行,没有任何反应。
分组导航标记。
#pragma mark 分组名
就会在导航条对应的位置显示一个标题。#pragma mark -
就会在导航条对应的位置显示一条水平分割线。#pragma - 分组名
在导航条对应位置先产生一条水平分割线,再显示标题
容易犯错的注意点
- @interface 和 @implement 他们之间不能相互嵌套
- 类必须要先声明再实现。类的声明和实现必要都要有
- 声明类的时候,类的声明和实现必须要同时存在。特殊情况下可以只有实现,没有声明,但是不规范
- 属性名一定要以下划线开头,这是规范。类名每个单词首字母大写
- 属性不允许声明的时候初始化
- 如果方法只有声明,没有实现。编译不会报错。new 一个对象去调用这个方法,会在运行时报错。
command + B xcode 编译。
类指针作为方法的参数,一般是指针,实参和形参指向同一个对象,形参的改变可以影响实参。类指针作为方法的返回值,返回指针,指向对象。
xcode 中,一个target 中类无法直接在另外一个 target 中访问
异常,try catch 语句。@finally 无论 try 中是否发生异常都会执行
1
2
3
4@try {
} @catch(NSException* ex) {
} @finally {
}并不是所有的异常都能处理,比如 C 语言的异常无法处理,除数为 0 、空指针解引用。
OC 方法,对象方法(声明使用 - 号)、类方法(声明使用 + 号)。
- 规范:一个类需要为这个类提供一个和类名同名的类方法,这个方法创建一个最纯洁的对象返回
instanceType 作为返回值 代表返回的是当前这个类的对象 。
- 将 C 语言字符串转换成 NSString。
+ (**nullable** **instancetype**)stringWithUTF8String:(**const** **char** *)nullTerminatedCString;
- 将 C 语言字符串转换成 NSString。
对属性赋值,可以提供对象方法,同时将属性设置为私有。规范:setPropertyName 。取出属性的值,规范:getPropertyName d
对象之间的关系。组合关系、依赖关系、关联关系、继承关系。
- 组合关系,一个类由其他几个类联合起来组合而成,他们之间的关系就叫做组合关系
- 依赖关系,一个对象的方法的参数是另外一个对象,那么就说他们的关系是依赖关系
- 关联关系,关联体现的是两个类之间语义级别的一种强依赖关系。一个类作为另外一个类的属性,是拥有的关系
Xcode 文档的安装
OC 中的 static 不能修饰属性,也不能修饰方法。可以修饰方法中的局部变量。这个局部变量会放在静态区。
self 关键字,self 是指针,在对象方法中指向当前对象,在类方法中指向当前类。在类方法中,self 指向当前这个类在代码段中的地址。
对象方法和类方法可以重名。
继承,子类拥有父类中所有成员。包括属性和方法,包括类方法和对象方法。
- 单根性,一个类只能有一个父类
- 传递性,A 类从 B 类继承,B 类从 C 类继承,那么A类则会继承 B 类和 C 类
- NSObject 是 Foundation 框架中的类,这个类有一个类方法 new。这个方法是用来创建对象的,方法的返回值是创建的这个对象的指针。还定义了属性 isa 指针,每一个继承此类的子类都有一个 isa 指针。
- OC 的类必须直接或间接的继承 NSObject 类,如果不继承,就无法创建对象。
- 子类中不能存在和父类同名的属性
super 关键字,可以用在类方法和对象方法中,调用父类的对象方法或者类方法,不能访问属性
- 在对象方法中可以使用 super 关键字调用当前对象从父类继承过来的对象方法
- 在类方法中 super 关键字可以调用从当前类从父类继承过来的类方法
访问修饰符,用来修饰属性
- @private 私有,只能在本类的内部访问
- @protected 受保护,只能在本类以及子类中访问
- @package 可以在当前框架中访问
- @public 公共,都可以访问
- 如果不为属性指定访问修饰符,那么默认的就是 @protected
- 如果把类的属性写到类的实现中,使用大括号括起来。外界不仅不能访问,而且也看不到这个属性。是一个私有属性,各种修饰符无效。
私有方法,方法不写声明,只写实现,那么这个方法就是一个私有方法。只能在本类其他方法中调用,不能被外界调用。
里氏替换原则。子类可以替换父类的位置,并且程序的功能不受影响。一个父类指针指向一个子类对象
- NSObject 对象指针可以存储任意的 OC 对象的地址
- 当一个父类指针指向一个子类对象的时候,通过这个父类指针就只能调用子类对象中的父类成员。子类独有的成员无法访问
子类重写父类的方法,只需要在子类的实现中重新实现即可
使用 %@ 打印一个对象时,NSLog 函数的底层实现
- 调用当前对象的 description 方法
- 拿到这个方法的返回值 这个方法的返回值是一个字符串
- 将这个字符串输出
- description 方法是定义在 NSObject 类之中的,所以每个 OC 对象都有这个方法。这个方法返回:
@<对象所属的类名:对象的地址>
- 可以重写 description 方法,打印出自己设定的当前类对象。
类在代码段中是如何存储的
- 先在代码段中创建 Class 对象,Class 是 Foundation 框架中的一个类。这个 Class 对象就是用来存储类信息的
- 将类的信息存储在这个 Class 对象之中。这个 Class 对象,至少有三个属性。类名:存储的这个类的名称;属性:存储的这个类具有哪些属性;方法:存储的这个类具有哪些方法
- 所以类以 Class 对象的形式存储在代码段的。存储类的这个 Class 对象,也叫做类对象,用来存储类的一个对象。所以存储类的类对象也有一个叫做 isa 指针的属性,这个指针指向存储父类的类对象。
- 调用类的类方法 class 就可以得到存储类的类对象的地址。
Class c1 = [Person class];
SEL 全称是 selector 选择器,是一个数据类型。其实是一个类,用来存储一个方法的。
- 如何将方法存储在类对象之中。先创建一个 SEL 对象,将方法的信息存储在这个 SEL 对象之中,在将这个 SEL 对象作为类对象的属性。
- 拿到存储方法的 SEL 对象。SEL 是 typdef 类型,已经是指针了。
SEL s1 = @selector(sayHi);
调用方法的本质。
[p sayHi]
内部的原理- 先拿到存储 sayHi 方法的 SEL 对象,也就是拿到存储 sayHi 方法的 SEL 数据(SEL 消息)
- 将这个 SEL 消息发送给 p 对象
- 此时,p 对象接收到这个 SEL 消息以后,就知道要调用方法
- 根据对象的 isa 指针找到存储类的类对象
- 找到这个类对象以后,在这个类对象中去搜寻是否有和传入的 SEL 数据相匹配。如果有,就执行;如果没有,在找父类,直到 NSObject
- 调用方法的本质,其实为对象发送 SEL 消息
手动为对象发送 SEL 消息
- 先得到方法的 SEL 数据
SEL s1 = @selector(sayHi);
- 将这个 SEL 消息发送给 p 对象
[p performSelector:s1];
其实就是调用 p 的方法
注意:
如果方法有参数,方法名会有冒号 ,并且使用其他发送 SEL
1
2SEL s2 = @selector(sayHiWithName:);
[p performSelector:s2 withObject:@"nihao"];如果方法有多个参数,那就把这些参数封装到一个类中。
- 先得到方法的 SEL 数据
点语法。
对象名.去掉下划线的属性名
点语法在编译器编译的时候,其实会将点语法转换为调用 setter、getter 的代码
1
2
3
4
5
6
7// setter 方法
- (void)setName:(NSString*)name;
p.name = name; // 调用点语法就会转换成 setName 方法
// getter 方法
- (NSString*)name;
NSString* str = p.name; // 调用点语法就会转换成 name 方法在 getter 和 setter 方法中慎用点语法,可能造成无限递归
setter 和 getter 方法的命名要符合规范,如果不符合,可能会出错
如果属性没有封装 setter 和 getter ,是无法使用点语法的
property 作用:自动生成 getter 和 setter 方法的声明。应该写在 @interface 类的声明中
- 语法:@property 数据类型 : 名称
@property int age;
- property 的类型和属性的类型一致。@property 的名称和属性的名称一致(去掉下划线)
- @property 的名称决定了生成的 getter 和 setter 方法的名称。@property 的数据类型决定了生成的 setter 方法的参数类型和 getter 方法的返回值类型
- @property 只是生成 getter 和 setter 方法的声明,实现还要自己来,属性还要自己定义
- 语法:@property 数据类型 : 名称
@synthesize 作用:自动生成 getter、setter 方法的实现,所以应该写在类的实现之中
- 语法 @synthesize @property的名称;
- 生成一个真私有的属性,属性的类型和 @synthesize 对应的 @property 类型一致。属性的名字和 @synthsize 对应的 @property 名字一致
- 自动生成 setter 方法的实现,实现的方式:将参数直接赋值给自动生成的那个私有属性。并且没有做任何的逻辑验证
- 自动生成 getter 方法的实现,实现的方式:将生成的私有属性的值返回
希望 @synthesize 不要自动生成私有属性,getter 和 setter 的实现中操作我们已经写好的属性就好了
- @syntheszie @property 名称 = 已经存在的属性名;
- 不会再去生成私有属性;直接生成 setter、getter 的实现。
- setter 的实现:把参数的值直接赋值给指定的属性
- getter 的实现:直接返回指定的属性的值
- 如果 setter 或 getter 有自己的逻辑验证,那就自己在类中重写即可
- 批量声明,如果多个 @property 的类型一致,可以批量声明
@property float height, weight;
- @synthesize 也可以批量声明,类型不一致也可以
@synthesize name=_name, age=_age, weight=_weight;
从 xcode 4.4 以后,xcode 对 @property 做了增强,只需要写一个 @property ,编译器就会自动生成私有属性(带下划线)、自动生成 getter、setter 的声明和实现。也可以批量声明
- 如果 setter 和 getter 同时重写,就不会自动生成私有属性,需要自己定义
- 如果 setter 和 getter 只有一个重写,另外一个编译器会自动生成
- 可以被继承,但是属性是私有的,不能直接访问,只能通过 setter 和 getter 方法访问
动态类型和静态类型
- 静态类型:指的是一个指针指向的对象是一个本类对象
- 动态类型:指的是一个指针指向的对象不是一个本类对象
- 编译检查,判断原则:看指针所属的类型之中有没有这个方法,如果有则通过,如果没有则报错。
NSObject 是 OC 中所有类的基类,因此 NSObject 是一个万能指针,可以指向任意的 OC 对象。缺点:如果要调用指向的子类对象的独有的方法,就必须要做类型转换。
id 指针,是一个万能指针,可以指向任意的 OC 对象。类似于
void*
- NSObject 和 id 的异同,都是万能指针,指向任意对象
- NSObject 指针去调用对象的方法时,编译器会做编译检查;通过 id 类型的指针去调用对象的方法时,编译器不会做编译检查。
- id 指针只能调用方法,不能使用点语法。
instancetype 代表方法的返回值是当前类的对象。instancetype 是一个有类型的、代表当前类的对象。id 是一个无类型的指针,是一个没有类型的指针。
判断指针指向的对象中是否有这个方法可以执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 判断指针指向的对象中是否有这个方法可以执行。
- (BOOL)respondsToSelector:(SEL)aSelector;
if ([p respondsToSelector:@selector(sayHi)]) {}
// 判断类中是否有指定的类方法
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
// 判断对象是否是 指定类的对象或者子类对象
- (BOOL)isKindOfClass:(Class)aClass;
if ( [p isKindOfClass:[Person class]]) {}
// 判断对象是否为指定类的对象,不包括子类
- (BOOL)isMemberOfClass:(Class)aClass;
if ( [p isMemberOfClass:[Person class]] ) {}
// 判断类是否为另外一个类的子类
+ (BOOL)isSubclassOfClass:(Class)aClass;new 方法的内部,其实是先调用 alloc 方法,在调用 init 方法。
- alloc 方法是一个类方法,作用:哪一个类调用这个方法,就创建这个类的对象,并把对象返回
- init 方法 是一个对象方法,作用:初始化对象
init 方法,为对象的属性赋初始值。也叫做构造方法。
- 重写 init 方法规范:
- 必须先调用父类的 init 方法,然后将方法的返回值赋值给 self 。
- 调用 init 方法初始化对象有可能会失败,如果失败,返回就是 nil。判断父类是否初始化成功,判断 slef 的值是否为 nil,如果不为 nil 说明初始化成功
- 如果初始化成功,就初始化当前对象的属性。最后返回 self 的值
1
2
3
4
5
6
7
8- (instancetype)init {
self = [super init];
if (self != nil) {
// initialize self
self.name = @"jack";
}
return self;
}- 为什么要调用父类的 init 方法?因为父类的 init 方法会初始化父类的属性,所以必须要保证当前对象中的父类属性也同时被初始化。
- 为什么要赋值给 self ?因为调用父类的 init 方法 会返回初始化成功的对象。实际上返回的就是当前对象,但是我们要判断是否为 nil
- 重写 init 方法规范:
自定义构造方法。规范
- 自定义构造方法的返回值必须要是 instancetype
- 自定义构造方法的名称必须以 initWith 开头
- 方法的实现和 init 的要求一样
引用计数器。用来记录当前对象的正在使用的次数
- 每一个对象都有一个 retainCount 的属性,叫做引用计数器,类型是 unsigned long,占据 8 个字节。
- 当这个对象的引用计数器变为 0 的时候,代表这个对象无人使用,运行时会自动回收这个对象。
如何操作引用计数器
- 为对象发送一条 retain 消息,对象的引用计数器就会加一
- 为对象发送一条 release 消息,对象的引用计数器就会减一
- 为对象发送一条 retainCount 消息,就可以拿到对象的引用计数器的值
内存管理的分类
- MRC:Manual Reference Counting 手动引用计数,手动内存管理。当多一个人使用对象的时候,要求程序员手动的发送 retain 消息。少一个人使用的时候程序员手动的发送 release 消息
- ARC:Automatic Reference Counting 自动引用计数,自动内存管理。系统自动的在合适的地方发送 retain release 消息
IOS 5 开始,Xcode 4.2 开始就支持 ARC。Xcode 默认 ARC 开发。关闭 ARC 开启 MRC。
Objective-C Automatic Reference Counting
设置为 NO- 重写 dealloc 方法的规范,必须要调用父类的 dealloc 方法,并且一定要放在最后。
1
2
3
4
5
6
7Person* p = [[Person alloc]init];
// 获取引用计数器的当前引用次数
NSInteger count1 = [p retainCount];
// 引用计数器加一
[p retain];
// 引用计数器减一
[p release];- 在 ARC 机制下,retain、release、dealloc 这些方法无法调用。
- MRC 机制下,当 release 后对象的引用计数器变为0,会被立即回收。
僵尸对象,一个已经释放的对象,但是这个对象所占的空间还没有分配给别人,这样的对象叫做僵尸对象。通过野指针去访问僵尸对象时,如果这块空间还没有分配给别人,那就没有问题。如果分配给别人了,那就会出现问题。
- 我们希望只要对象是僵尸对象,无论如何都不允许访问。xcode 提供僵尸对象的实时检查机制。
target -> edit scheme -> Run -> Diagnostics -> Zombie object
- xcode 默认不打开僵尸对象检测。打开之后,再每打开一个对象时,都会先检查是否为僵尸对象,比较消耗性能
- 如何避免僵尸对象错误,当一个指针成为野指针以后,将这个指针的值设置为 nil 。当一个指针指为 nil,通过这个指针去调用对象的方法(包括使用点语法)的时候,不会报错,只是没有任何反应。但是如果直接访问属性,就会报错。
- 我们希望只要对象是僵尸对象,无论如何都不允许访问。xcode 提供僵尸对象的实时检查机制。
内存泄露,单个对象的内存泄露的情况
- 有对象的创建,而没有对应的 release
- retain 的次数和 release 的次数不匹配
- 在不适当的时候,为指针赋值为 nil
- 在方法中为传入的对象进行不适当的 retain
在 MRC 机制下, 当属性是一个 OC 对象时,这个属性的 setter 方法的写法
1
2
3
4
5
6
7
8
9
10
11- (void)setCar(Car*)car {
if (_car != car) {
[_car, release];
_car = [car retain];
}
}
// 重写 dealloc 方法
- (void)dealloc {
[_car release];
[super delloc];
}@property 参数。这几个参数没有顺序
- 与多线程相关的两个参数:atomic、nonatomic
- atomic:生成的 setter 方法会加锁,线程安全,效率低
- 与生成的 setter 方法的实现相关的参数:assign、retain
- assign:默认值。生成的 setter 方法就是直接赋值
- retain:生成的 setter 方法就是标准的 MRC 内存管理代码。也就是先判断新旧对象是否为同一个对象,如果不是 release 旧的,retain 新的。当属性的类型是 OC 对象类型时使用 retain。非 OC 对象使用 assign。
- retain 参数只是生成标准的 setter 方法(MRC内存管理代码),但是不会生成dealloc 中 release 的代码,所以我们还要自己手动在 dealloc 中 release
- 与生成只读、读写相关的参数:readonly、readwrite
- readwrite:默认值,会同时生成 setter、getter 方法
- readonly:只会生成 getter,不会生成 setter
- 与生成的 getter setter 方法名字相关的参数:getter、setter
- 默认情况下,@property 生成的 getter、setter 方法的名字都是标准的名字
- 其实可以通过参数来指定 @property 生成的 getter 方法的方法名字
- 注意:指定 setter 方法的名字时,因为 setter 方法带参数,因此方法名需要加上冒号
(:)
- 如果使用 getter、setter 修改了生成方法的名字,在使用时,编译器会自动转换
- 与多线程相关的两个参数:atomic、nonatomic
当两个类相互包含的时候,会出现循环引用的问题。解决方案:其中一边不要使用 #import 引入对方的头文件,而是使用 @class 类名; 来标注这是一个类。这样子就可以在不引入对方头文件的情况下,告诉编译器是一个类。可以在 .m 文件中再 #import 对方的头文件,就可以使用。
- @class 和 #import 的区别
- #import 是将指定文件的内容拷贝到写指令的地方
- @class 并不会拷贝任何内容。只是告诉编译器这是一个类
- @class 和 #import 的区别
循环 retain 。当两个对象互相引用的时候,A 对象的属性是 B 对象,B 对象的属性是 A 对象。
- 解决方案:一端使用 retain,另外一端使用 assign。使用 assign 的那一端在 dealloc 中不再需要 release
自动释放池。存入到自动释放池的对象,在自动释放池被销毁的时候,会自动调用存储在该自动释放池中的所有对象的 release 方法
1
2
3// 创建自动释放池
@autoreleasepool {
}如何将对象存储到自动释放池中?在自动释放池之中调用对象的 autorelease 方法,就会将这个对象存入到当前自动释放池之中。这个 autorelease 方法返回的是对象本身,所以可以这么写
1
2
3@autoreleasepool {
Person* p1 = [[[Person alloc] init] autorelease];
}自动释放什么时候结束。@autoreleasepool 大括号范围结束时。
注意点
- 只有在自动释放池中调用了对象的 autorelease 方法,这个对象才会被存储到这个自动释放池
- 对象的创建可以在自动释放池的外面
- 当自动释放池结束时,仅仅是对存储在自动释放池的对象发送一次 release 消息,而不是销毁对象
- 如果在自动释放池中,调用同一个对象的 autorelease 方法多次,就会将对象存储多次到自动释放池中。在自动释放池结束时,会发送多条 release 消息
- autorelease 方法和 对象的 release 和 retain 不冲突,都会起作用
- 自动释放池可以嵌套
类方法的规范,apple 的框架中的类也遵守这个规范
- 一般情况下,要求提供与自定义构造方法相同功能的类方法,这样可以快速创建一个对象
- 使用类方法创建的对象,要求这个对象在方法中就已经被 autorelease 过了。
ARC(Automatic Reference Counting)自动引用计数。系统自动的帮助我们去计算对象的引用计数器的值。
当 ARC 开启时,编译器会自动的在合适的地方插入 retain、release、autorelease 代码。
ARC 机制下,只要没有强指针指向这个对象,这个对象就会立即回收。本质:对象的引用计数为 0 的时候,自动释放。
强指针:默认情况,声明一个指针就是强指针。也可以使用
__strong
来显式声明这是一个强指针。1
2Person* p;
__strong Person* p;弱指针:使用
__weak
标识的指针就是弱指针。1
__weak Person* p;
强弱指针唯一的区别就是 ARC 模式下,他们用来作为回收对象的基准。
两种情况叫做没有任何强指针指向对象
- 指向对象的所有强指针被回收掉
- 指向对象的所有强指针赋值为 nil
在 ARC 机制下,当对象被回收的时候,原来指向这个对象的弱指针会自动置为 nil
ARC 机制下,@property 参数不能使用 retain,因为 retain 会生成属性标准的 getter 代码,ARC 机制不需要这些逻辑。
@property 生成的属性是强指针还是弱指针(strong、waek)。
在 ARC 机制下,当两个对象互相引用的时候,如果两边都使用 strong,那么就会出现内存泄露。建议一端使用 strong、一端使用 weak
ARC 和 MRC 机制如何兼容?在
targets -> Build Phases -> MRC的类 -> 右击
,填写-fno-objc-arc
即可。xcode 可以将整个 MRC 程序转换为 ARC 程序
Edit -> Convert -> To Objective-C ARC
。建议不要转,xcode 无法从 ARC 转成 MRCcategory 。如果一个类模块有太多的方法,就显得很臃肿、维护成本增加。category 可以将一个类分成多个模块。
添加 oc 文件,选择 category 文件类型,以及对应类。会生成一个 .h 和一个 .m 的模块。
- 模块的文件名:
本类名+分类名.h 本类名+分类名.m
- 模块的文件名:
添加的 category 也分为声明和实现
1
2
3
4@interface 本类名 (分类名)
@end
代表不是新创建一个类,而是对已有的类添加一个分类,小括号中写上这个分类的名字
因为一个类可以添加多个分类,为了区分每一个分类,所以分类要取名字category 的使用。如果要访问分类中定义的成员,需要把分类的头文件引进来
category 的注意:
- category 只能增加方法,不能增加属性
- 在 category 中可以写 @property ,但是不会自动生成私有属性。也不会自动生成 getter、setter 的实现,只会生成 getter、setter 的声明
- 在 category 的方法实现中不可以直接访问本类的真私有属性(定义在本类的 implementation 之中),但是可以调用本类的 getter、setter 方法来间接访问属性
- 当分类中有和本类中同名的方法时,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后编译的分类。
非正式协议:为系统自带的类写分类。相当于为已经存在的类添加方法。对全局都会生效
ARC 机制和其他语言的垃圾回收机制的区别
- ARC 不是运行时,是在编译的时候就在合适的地方插入 retain、release 等,当引入计数器为 0 时,立即销毁。
- ARC 是编译时,其他语言的拉取回收机制是运行时
首页 | 归档 | 分类 | 标签 | 关于 |
|