一、僵尸对象(Zombie Objects)
1.概述
1.1-》僵尸对象:已经释放掉的对象。通常来讲,如果我们访问已经释放掉的对象或者向他发送消息的话都会引起错误的。因为指针指向的内存块认为我们无权访问或者它无法执行该消息,所以内核会抛出一个异常(EXC),表示我们不能访问该存储区域(BAD ACCESS).(EXC_BAD_ACCESS类型错误)
1.2-》调试解决该类问题通常采用NSZombieEnabled(开启僵尸模式)。
2、NSZombieEnabled
2.1-》Xcode提供的NSZombieEnabled,通过生成僵尸对象来替换dealloc的实现,当对象引用计数为0 的时候,将需要dealloc的对象转化为僵尸对象。如果之后再给这个僵尸对象发消息则抛异常。先选中Product -> Scheme -> Edit Scheme -> Diagnostics -> 勾选Zombie Objects 项,显示如下:
然后在Product -> Scheme -> Edit Scheme -> Arguments设置NSZombieEnabled、MallocStackLoggingNoCompact两个变量,且值均为YES。显示如下:
2.2->仅设置Zombie Objects的话,如果Crash发生在当前调用栈,系统可以把崩溃原因定位到具体代码中;但是如果Crash不是发生在当前调用栈,系统仅仅告知崩溃地址,所以需要添加变量。MallocStackLoggingNoCompact,让Xcode记录每个地址alloc的历史,然后通过命令将地址还原出来。
注意:发版前要将僵尸对象检测这些设置都去掉,否则每次通过指针访问对象时,都去检查指针指向的对象是否为僵尸对象,这就影响效率了。
3.代码中的注意事项
在ARC时代,避免访问释放掉的内存,代码需要注意的地方有:
3.1-》检查代码1:
不能使用assgin或unsafe_unretained修饰指向OC对象的指针
assign或unsafe_unretained都表示不持有对象,是弱引用。如果指针指向的对象被释放了,他们就变成了野指针,很有可能发生Crash.
建议1:assign仅用于修饰NSInteger等OC基础类型,以及short、int、double、结构体等C数据类型,不修饰对象指针;
建议2: OC对象属性一般使用strong关键字(默认)修饰。
建议3: 如果需要弱引用OC对象,建议使用weak关键字,因为被weak指针所引用的对象被回收后,weak指针会被赋为nil(空指针),给nil发任何消息都不会出问题。使用weak修饰代理对象属性就是很好的例子。
3.2-》检查代码2:
Core Foundation等底层操作:Core Foundation等底层操作它们不支持ARC,还需要手动内存管理。
二:野指针:(Wild pointer)
1.概述
1.1—》
野指针是指向一个已经删除的对象或未申请访问受限内存区域的指针。而这里的野指针主要是指对象释放后,指针未置空导致的野指针。该类Crash发生比较随机,找出来比较费劲,比较常见的做法是在开发阶段就提高这类Crash的复现率,尽可能的将其发现并解决。
1.2-》
向OC对象发出release消息,只是标记对象占用的那块内存可以被释放,系统并没有立即收回内存;如果此时还向该对象发送其他消息,可能会发生Crash,也可能没有问题。野指针造成的Crash随机性比较大,但是被随机填入的数据是不可访问的情况下,Crash是必现的。解决思路是:想办法给野指针指向的内存填写不可访问的数据,让随机的Crash变成必现的Crash。
2、设置Malloc Scribble
Xcode提供的Malloc Scribble,可以将对象释放后在内存上填上不可访问的数据,将随机发生变成不随机发生的事情,选中Product->Scheme->Edit Scheme ->Diagnostics – >勾选 Malloc Scribble项,结果如下:
设置了Enable Scribble,在对象申请内存后在申请的内存上填0xaa,内存释放后在释放的内存上填0x55;如果内存未被初始化就被访问,或者释放后被访问,Crash必现。
特别注意:
该方法必须连接Xcode运行代码才能发现,并不适合测试人员使用。
3.代码中的注意事项
检查使用assgin或 unsafe_unretained 修饰指向OC对象的指针 和 Core Foundation等底层操作。
三、内存泄漏
1.代码中的注意事项(ARC下的循环引用是内存泄漏的主要原因)
1.1-》检查代码1 :Core Foundation、Core Graphics等操作
Core Foundation、CoreGraphics等操作不支持ARC,还需要手动内存管理。
建议: 注意CF、CG对象的创建和释放。
1.2-》检查代码2 :NSTimer/CADisplayLink的使用,因为NSTimer/CADisplayLink对象的target会强引用self,而self又强引用NSTimer/CADisplayLink对象。
建议:使用扩展方法,使用block或 target弱引用目标对象 打破保留环,具体实现参考iOS实录8:解决NSTimer/CADisplayLink的循环引用
1.3-》检查代码3 :block使用代码。
建议:成对使用weakSelf和strongSelf来打破block循环引用(对于self没有引用的block是不会造成循环引用,不需要使用weakSelf和strongSelf)
原理:在block外定义弱引用(weakSelf),指向的self对象;在block内捕获的是这个弱引用(weakSelf),保证了self不会被block所持有;在执行block内方法时,生成强引用(strongSelf),指向了弱引用(weakSelf)所指向的对象(self对象);在block内部实际是持有了self对象,但是这个强引用(strongSelf) 的生命周期只在这个block执行的过程中,block执行执行完立刻就被释放了。