对于 iOS 的内存管理方式,大家一般都是只知其然而不知其所以然。下面就对 iOS 的内存管理做一个简单的总结。
iOS 内存管理的基本原则
iOS 的内存管理主要遵循以下的几个基本原则,这个原则无论在 ARC 时代还是在 MRC 时代都适用的:
- 自己生成的对象,自己持有;
- 不是自己生成的对象,自己也可以持有;
- 自己不再需要持有对象时,进行释放;
- 不能释放自己不持有的对象。
自己生成的对象,自己持有
在 iOS 内存管理中有这样的四个关键字: alloc、new、copy、mutableCopy,如果自身通过这几个关键字产生了对象,那么自身就持有了这个对象:
1 | id obj = [[NSObject alloc] init]; |
使用了 alloc 或者 new 分配了内存,obj 指向了这个对象,这个对象本身的引用计数是1,不需要再进行 retain 操作。
不是自己生成的对象,自己也可以持有
1 | id obj = [NSMutableArray array]; |
NSMutableArray 通过类方法 array 产生了对象,这个对象不属于 obj 自身产生的。所以,这个时候需要使用 retain 方法使对象的引用计数+1,此时尽管这个对象不是 obj 自身产生的,但是它也可以持有这个对象了。
自己不再需要持有对象时,进行释放
1 | id obj = [NSMutableArray array]; |
当 obj 不需要再继续持有对象的时候,就可以发送 release 消息了。
不能释放自己不持有的对象
1 | id obj = [[NSObject alloc] init]; |
释放一个已经被释放的对象是不允许的。
1 | id obj1 = [obj object]; |
obj1 在没有进行 retain 操作的情况下进行了 release 操作,使得 obj 持有的对象被释放了,这样会造成野指针,也是不允许的。
引用计数
iOS 对于引用计数的管理是通过一个散列表来完成的。这个散列表的键值是对象的内存块地址的散列值。在操作引用计数器时,就是对引用计数的散列表进行操作:只要获取到这个散列表的地址和相应的对象的内存地址,就可以通过对象的内存地址在散列表中进行索引找到引用计数的值,然后根据用户的操作返回引用计数器,或者对引用计数器进行+1或者-1的操作。