#program mark - 01 内存管理的概述 [掌握] 1.内存中有五大区域,为什么只需要关心堆区? 1)堆区存的是对象,对象占的区域可能会很大 2)堆区的对象不会自动释放,需要手动管理,如果不进行管理,那么会直到程序结束的时候才会释放
2.什么时候释放堆区的对象 1)当这个OC对象没有"人"使用的时候,可以释放 2)当这个OC对象有"人"使用的时候,千万不要释放 注意:"人"表示对象的使用者
3.如何判断一个OC对象是否有人使用 1.每一个OC对象都有一个引用计数器(retainCount),表示对象的使用者的个数 2.每当创建一个新的对象,retainCount值默认是1 3.每当多一个人要使用对象的时候,retainCount+1 4.每当少一个人要使用对象的时候,retainCount-1 5.当retainCount=0的时候,系统会立即回收该对象,同时给对象发送dealloc消息
4.程序员如何控制retainCount值? 1.向对象发送release消息,让对象的retainCount-1 2.向对象发送retain消息,让对象的retainCount+1 3.向队形发送retainCount消息,获得对象的retainCount值 4.所谓的消息,其实就是对象的方法 所有和内存管理相关的成员变量以及方法,都是定义在NSObject类中的
5.内存管理的分类 1.MRC,手动引用计数,程序员需要手动管理对象的retainCount值, 2.ARC,自动引用计数,编译器帮我们管理对象的retainCount值 3.ARC和MRC的关系 1.ARC是基于MRC的 2.ARC是编译器特性,编译器在编译的时候,会在合适的位置,自动插入内存管理的代码 3.ARC是2011年iOS5出现,是程序员的一大福音,省去了很多繁琐的内存管理的工作 4.从Xcode6开始,新建的项目默认都是ARC的
7.MRC是ARC的底层原理 1.面试肯定会问MRC相关的问题 2.只有理解MRC才能更好的理解内存管理的原理
#program mark - 02 第1个MRC程序 [掌握] 1.如何关闭ARC机制,把项目设置为MRC 在项目信息页面,选择BuildSettings,搜索auto,找到"A...R...C...",默认是YES,改成NO即可. 注意:如果在ARC环境下,不能使用内存管理的方法,否则编译器会报错.
2.为什么我们重写dealloc方法的时候,需要在最后调用父类的dealloc方法? 重写init方法的时候 1.父类的init方法实现默认有一些初始化对象时必须的操作 2.需要在父类init方法的基础之上增加一些操作,这个时候需要先调用父类中init方法实现
重写dealloc方法的时候 1.父类的dealloc方法实现中默认有一些对象释放的是否必须的操作,一旦调用了NSObject类中dealloc方法的实现,那么对象就会被彻底的释放 2.我们在对象彻底释放之前可能还需要对对象做一些事情,比如说需要先释放当前对象中特有一些内容,在完成这些操作之后,再调用父类中dealloc方法的实现
3.注意点: 1.重写dealloc方法的时候,必须在最后调用父类中dealloc方法的实现(MRC) 2.dealloc方法不需要手动调用,当对象销毁的时候系统会自动调用该对象的dealloc方法,千万不要手动调用 3.对象调用release方法之后就会销毁吗? 不一定,release方法只会让当前对象的引用计数器-1,和销毁对象之间没有什么必然联系 在MRC中判断对象是否销毁只有一个原则,就是对象的引用计数器是否为0
#program mark - 03 内存管理的原则 [掌握]
1.保持平衡 1.有new\alloc,就必须要有release 2.有retain,就必须要有release 3.谁new\alloc,谁release 4.谁retain,谁release
// 3.使用哪个指针给对象发送了new/alloc消息,就需要通过这个指针发送release // 4.使用哪个指针个对象发送了retain消息,就需要通过这个指针发送release
HMPerson *p1 = [[HMPerson alloc] init]; //1 //[p1 release];
//HMPerson *p2 = p1; //[p2 retain]; // retain内部把当前对象的retainCount+1,然后返回当前对象
HMPerson *p2 = [p1 retain]; // 2
[p1 release] // 1 [p2 release] // 0 -> 由系统调用dealloc方法,释放对象
// //HMPerson *p2 = [p1 retain]; //HMPerson *p3 = p1; // //[p1 release]; //[p2 release];
#program mark - 04 野指针与僵尸对象 [掌握]
在OC中
1.对象的回收 1.一旦对象回收,说明对象在内存中占有的堆区空间,可以分配给别人使用(不代表空间被删掉,也不代表立刻分配给被人使用) 2.但是在系统把该空间分配给其他人使用之前,对象还存在(对象中的数据还存在)
2.僵尸对象 对象已经被回收了,但是对象的数据还存在内存中,这样的对象叫做僵尸对象 注意点: 1.当僵尸对象占用的空间,系统没有分配给其他人使用的时候,这个对象其实还存在,还可以访问 2.当僵尸对象占用的空间,系统已经分配给其他人使用的时候,这个对象不存在,不可以访问 3.严格意义上来说,对象一旦成为了僵尸对象,无论如何不能访问 4.僵尸对象无法复活(不应该调用僵尸对象的方法)
Xcode中开启僵尸对象检测 默认没有开启,避免浪费性能 Edit Scheme -> Diagnostics -> 勾选Enable Zombie Object
3.野指针 指向被回收对象的指针,叫做野指针 指向堆区僵尸对象的栈区指针变量,叫做野指针
4.如何防止野指针错误 HMPerson *p1 = [[HMPerson alloc] init]; // 1 [p1 release] // 0 // p1是一个野指针,p1指向的堆区对象是僵尸对象 p1 = nil;
[nil sayHi];
#program mark - 05 单个对象的内存管理 [掌握]
int main() { HMPerson *p1 = [HMPerson new]; }
1.如何避免内存泄漏 1.retain和release一定要匹配 2.不要让指针随便指向nil
#program mark - 06 多个对象的内存管理之一 [掌握] 1.当对象A中拥有一个成员变量是对象B,如果A没有释放,那么B就不能是释放.因为B作为A的成员变量,说明A随时都有可能使用B,那么必须当A释放的时候,才能把B释放.
演化步骤: 1.创建对象A, 创建对象B 2.把B对象设置给A对象, 出现的问题:当B对象释放之后,A对象就无法再使用B对象 解决方案: 3.在把B对象设置给A的时候,让B对象的引用计数器+1,说明A对象拥有B对象 出现的问题:当A对象释放之后,B对象无法释放 解决方案: 4.当A对象销毁的时候,也就是在A对象的dealloc方法中,先让B对象的引用计数器-1,再释放A对象
#program mark - 07 多个对象的内存管理之二 [掌握] 出现的问题: 如果给A类对象a中的B类型的成员变量更换了一个值,从b对象换成了b1,此时在a对象销毁的时候,释放的是b1,而无法释放b,导致b内存泄漏 解决方案: 5.在B类型成员变量的set方法中,先release旧对象b,再retain新对象b1
#program mark - 08 回收分析 [掌握] 两个A类型的不同对象a和a1,同时拥有一个B类型的成员和变量b,没有出现任何问题
#program mark - 09 多个对象的内存管理之三 [掌握] 出现的问题: 如果给A类对象a中的B类型成员变量重复赋同一个值,之前是b,再赋一次b 因为在B类型的成员变量set方法中,是先release再retain,此时有可能导致b对象在release之后已经释放,变成僵尸对象,再进行retain操作会造成野指针错误 解决方案: 6.在B类型成员变量的set方法中,需要进行判断,如果是相同对象什么都不做,如果不是相同对象,先release旧对象,再retain新对象.
- (void)setB:(B *)b { if (_b != b) { [_b release]; _b = [b retain]; } }
- (void)dealloc { [_b release]; [super dealloc]; }
MRC环境下,对于OC对象的成员变量,如何重写set方法 在面试之前,一定要复习
#program mark - 10 当属性的类型是OC对象的时候,setter方法的写法 [掌握]
注意: 当成员变量是OC对象的时候,它的set方法才需要按照以上总结的内容来写. 当成员变量是基本数据类型的时候,直接赋值即可.
#program mark - 11 @property参数之与多线程相关的 [掌握] 多线程相关 1. atomic(默认值) 安全的 2. nonatomic 不安全的
#program mark - 12 @property参数之与生成setter方法实现相关的 [掌握] 在MRC下和内存管理相关的参数 assign(默认) retain
如果成员变量是OC对象,使用retain 如果成员变量是非OC对象,使用assign
#program mark - 13 @property其他参数 [掌握] 1.读写相关的 1.readwrite(默认),可读可写,会同时生产getter和setter 2.readonly,只读,只会生产getter,而不会生成setter
2.指定setter和getter名字 注意: 1.如果需要设置setter的名字,一定要加:.但是无论如何都不要更改setter的名字 2.如果成员变量是BOOL类型,getter一般以is开头,增加可读性 3.只是改变了setter和getter的名字,并没有改变内部实现
#program mark - 14 @property参数使用注意 [掌握]
1.以后在使用@property的时候,必须要加参数 1.每种类型的参数,同时只能存在一个 2.至少需要两类:线程相关的,内存管理相关的 规范写法:@property (nonatomic, retain) NSString *name;
2.如果在MRC环境下,当使用@property定义属性的时候,如果使用reatin,只会在setter中生成标准的MRC内存管理代码 但是dealloc中对该成员变量,并没有release,需要我们自己来做
|