为什么要进行内存管理
由于移动设备的内存是及其有限的,所以每个APP所占的内存也是有限制的,当APP所占用的内存较多时,系统就会发出内存警告,此时需要回收一些不再需要使用的内存空间,比如回收一些不再使用的对象和变量等。
内存的管理范围:任何继承NSObject的对象,对其他的基本数据类型无效。
内存的本质原因:因为对象和其他数据类型在系统中的存储空间不一样。其他局部变量主要存放于栈中,而对象存放于堆中,当代码块结束时这个代码块中的所有局部变量都会被回收,指针对象的指针也被回收,此时对象已经没有指针指向,但是依然存在于内存中,这样就会造成内存泄漏。
内存中的五大区域
- 栈:局部变量。当局部变量的作用域被执行之后,这个局部变量就会被系统立即回收。
- 堆:OC对象。使用C函数申请的空间。直到程序结束时才会被回收。
- BSS段:未初始化的全局变量、静态变量。一旦初始化就被回收,并转存到数据段中。
- 数据段:已经初始化的全局变量、静态变量。直到程序结束的时候才会被回收。
- 代码段:代码。程序结束的时候,系统会自动回收存储在代码段中的数据。
注意:
1.栈、BSS段、数据段、代码段存储在他们中的数据的回收,是由系统自动完成的,不需要我们手动干预。
2.存储在堆中的OC对象,系统不会自动回收。
内存管理的分类
- MRC:Manuall Reference Counting,手动计数器,手动管理内存,iOS 5之前使用。(当多1个人使用对象的时候,要求程序员手动发送retain消息,少一个人使用的时候程序员手动的发送release消息)
- ARC:Automatic Reference Counting,自动引用计数,自动管理内存,iOS 5推出的新功能,Xcode7开始系统默认支持的是ARC内存管理机制。(系统自动的在合适的地方发送retain、release消息)
因为Xcode7开始默认支持ARC内存管理机制,所以如果要关闭ARC开启MRC的话,步骤
点击这个项目的TARGETS,点击Build settings,接着在右上角搜索输入Automatic,发现在Apple Clang - Language - Objective - C下显示了Objective - C Automatic Reference Counting,将选项处的Yes改成NO,就说明已经关闭了ARC内存管理机制,从而开启了MRC内存管理机制。
引用计数器的作用
判断对象要不要回收的唯一依据就是引用计数器是否为0,若不为0则存在,为0的话就是对象的内存已经被回收了,对象已经不存在了。
引用计数器的相应操作
给对象发送消息,进行相应的计数器操作:
1.发送retain消息:引用计数器+1(表示该对象多一个人使用)
2.发送release消息:引用计数器-1(表示该对象该对象少一个人使用)
3.retainCount:获取对象当前的引用计数。(若引用计数为0时,表示该对象被回收)
注意
- 当创建并初始化一个对象时,默认它的引用计数为1,当这个对象的引用计数0时,系统会自动回收这个对象,在系统回收对象的时候,会自动的调用系统的dealloc方法。
- 重写dealloc方法时的规范:必须要调用父类的dealloc方法[super dealloc],并且这句代码要放在最后一行。
- 在ARC内存管理机制下,retain、release、dealloc这些方法都无法使用。
内存管理的原则:
- 有对象的创建,就要匹配一个release。
- retain的次数要和release的次数一致。
- 谁用谁retain,谁不用谁release。
- 只有在多一个人使用的时候才retain,少一个人使用的时候才release。
总结:
有始有终,有加就有减,有retain就应该匹配一个release,一定要平衡。
野指针和僵尸对象
- C语言中的野指针:定义一个指针变量,没有初始化。这个指针变量的值是一个垃圾值,指向一块随机的内存空间,这个指针就叫做野指针。
- OC中的野指针:指针指向的对象已经被回收了。
- 僵尸对象:一个已经被释放的对象,但是这个对象所占的空间还没有分配给别人。(所占内存已经被回收的对象)
注意
- 我们通过野指针去访问僵尸对象的时候,有可能是有问题的,也有可能是没有问题的。
- 当僵尸对象占用的空间还没有分配给别人的时候,这是没问题的。
- 当僵尸对象占用的空间已经分配给别人的时候,这个是会有问题的。(编译器会报错显示僵尸对象错误)
- 系统是不会默认打开僵尸对象检测的。(因为打开僵尸对象检测及其消耗性能)
- 如果需要开启僵尸对象检测的话,步骤:
点击Edit Scheme,在Run运行期这一栏点击Diagnostics(诊断结论),接着找到Zombie Objects这个选项,将它勾选上,僵尸对象检测就开启了。
如何避免僵尸对象报错
- 当一个指针为野指针后,将这个指针的值设置为nil。当一个指针的值为nil,通过这个指针去调用对象的方法(包括使用点语法)的时候,不会报错,只是没有任何反应。但是如果通过指针直接访问属性(p1->_name = @“Jack”)的话,就会报错。
- 当1个对象使用release方法,这个对象的引用计数已经为0时,不能使用retain方法重新去复活一个僵尸对象。(无法复活一个僵尸对象)
内存泄露
1个对象没有被及时回收,在该回收的时候没有被回收,一直驻留在内存中,直到程序结束的时候才被回收。
内存管理代码规范
- setter方法的代码规范:
- (void)setCar:(Car *)car {
//1.判断新旧对象是否为同一个对象
if (_car != car) {
//2.对旧对象做一次release
[_car release];//若没有旧对象,则没有影响
//3.对新对象做一次retain
_car = [car retain];
}
_car = car;
}
- dealloc方法的代码规范:
-(void)dealloc {
// NSLog(@"人死了");
[_car release];
[super dealloc];
}
注意
我们内存管理的范围是OC对象,所以,只有属性的对象是OC对象的时候,这个属性的setter方法才要想上面那样写,如果属性不是OC对象的,setter方法直接赋值就可以了。