目录

iOS中的内存管理

iOS中的内存管理

​ 小心内存管理,小心内存管理,小心内存管理!需要不断积累和深入思考才能写出高质量的安全代码。内存管理直接决定着程序的安全性和稳定性,内存管理核心的话题主要包括:循环引用、内存泄露、内存管理规则和垃圾回收机制等。

内存管理

内存泄漏和安全释放

​ 内存泄露指的是动态分配内存的对象在使用完后没有被系统回收内存,导致该对象始终占用内存,又无法通过代码访问,属于内存管理出错,如果出现大量内存泄露,那么会导致系统内存不足的问题。

​ 安全释放指释放掉不再使用的对象的同时不会造成内存泄露或指针悬挂问题的操作。为了保证安全释放,在对象dealloc后要将其指针置为nil。另外要严格遵守内存管理原则,保证对象的引用计数正确,同时要注意避免引用循环的出现。

​ 要求我们养成良好的编程习惯,可以借助Xcode的Instruments调试工具检测有内存泄露隐患的代码。

僵尸对象、野指针、空指针

​ 僵尸对象:引用计数为零的OC对象被释放后就变成僵尸对象,内存已经被系统回收,虽然对象还可能存在,数据依然在内存中,但是已经是不稳定对象了,不可以再访问和使用。僵尸对象所占内存是正常的,不会造成内存泄露。

​ 野指针:又称为"悬挂指针",野指针出现的原因是指针没有赋值,或者原指向的对象被释放了。野指针指向随机的一块垃圾内存,向它们发送消息会报EXC_BAD_ACCESS错误导致程序崩溃。

​ 空指针:不同于野指针,它是没有指向任何内容的指针,但是是有效指针,其值为nil、NULL、0、Nil等,向其发消息不会报错。

GC与RC

​ GC即垃圾回收机制Garbage Collection,它是宏观地对整体进行内存管理,将所有对象看作一个集合,然后在GC循环中定时检测活动对象和非活动对象,及时将用不到的非活动对象释放掉以避免内存泄露。

​ RC即引用计数Reference Counting,是局部性的,OC中每次Runloop都会检查对象引用计数,引用计数为0后会被系统马上释放掉。而自动释放池像一个局部的垃圾回收,将部分垃圾对象集中释放,相对于单个释放会有一定的延迟。

​ OC支持垃圾回收机制,但是有平台局限性,仅限于Mac桌面开发中,而在移动设备开发中不支持垃圾回收机制。移动设备开发中的内存管理采用MRC手动引用技术或ARC自动引用计数,通过计数引用的方式来管理内存的分配和释放。

alloc、dealloc、retain、release

​ alloc:创建对象,刚创建的对象默认引用计数为1,相当于在alloc创建过程中调用一次retain使引用计数+1。

​ dealloc:销毁对象,在对象引用计数为0的时候系统自动调用。

​ retain:保留对象,使对象引用计数+1。

​ release:释放对象,使对象的引用计数-1。

​ 需要注意retain、release方法只能在MRC下可以使用。

内存管理机制

block中避免引用循环

​ 在一个对象中强引用了一个block,在该block中又强引用了该对象,就会出现该对象和该block的循环引用。解决这个引用循环一般有两种方法:一种是将一方强制置为nil,破坏循环;另一种是使用_ _ weak或 _ _block修饰对象。

CAAnimation的代理是强引用

​ CAAnimation的代理是强引用,这是一个特例,因为CAAnimation动画是异步的,不是强引用可能会使其随时被释放掉。使用动画时需要采取措施避免循环引用,例如及时合适时机(在视图移除之前)移除动画。

手动释放的法则

​ 在MRC中手动管理内存,基本原则是:谁创建,谁释放,谁引用,谁管理。即使用new、alloc、copy关键字和retain了的对象等需要手动释放掉,但设置了autorelease的对象不需要手动释放。注意数组的add操作时添加成员对象的强引用。

​ MRC下静态方法创建的对象会自动进入自动释放池而不需要手动释放。例如创建一个NSString对象时注意,字面量(或被弃用的WithString)方法创建创建的是不可变字符串,位于常量内存区,可以认为已经被autorelease了,而stringWithFormat和initWithFormat方法创建的是在堆上,MRC下要手动管理内存。

autoreleasepool

​ autoreleasepool,即自动释放池,其实没有自身的结构,是基于多个AutoreleasePoolPage(一个C++类)以双向链表组合起来的结构,所以它的简单操作都是封装了AutoreleasePoolPage的操作结构。自动释放池位于栈上,符合先进后出原则。对比release操作的立即-1,autorelease操作是在对象的使用真正使用后才做引用计数-1,它的底层代码就是将对象添加到自动释放池。注意对一个对象连续两次调用autorelease后,由于自动释放池将其释放了不止一次,第二次释放时它成为了野指针,会导致崩溃。

​ 向自动释放池发送drain和release(release在支持GC的系统中不会引起回收操作)会清理自动释放池,即向池内所有对象发送release消息,故一般使用系统兼容性更强的drain消息来清理自动释放池。对于每一个新的RunLoop,系统都会隐式地创建一个自动释放池,RunLoop结束后自动释放池便会对对象进行释放操作。

其他注意事项

​ 在NotificationCenter中,添加对象到通知中心进行通知注册,通知中心只保存该对象的地址,但是没有强引用,如果这个对象释放了却没有在通知中心remove,那么通知发生时向保存的这个对象的地址发消息会导致崩溃。

​ NSArray的Copy是浅拷贝,MutableCopy是深拷贝;NSMutable的Copy和MutableCopy都是深拷贝。