iOS 内存管理
iOS 的引用计数就类似于下图中进出办公室的开关灯流程。当一个人A进入前,办公室的引用数为0,进入后需要照明,因此开灯,引用数为1,B进入后,引用数为2,C 进入后引用数为3,以此类推。A 离开时,引用数-1,当最后一个人离开办公室时,引用数为0,不再需要照明,因此关灯。
开关灯对应到 OC 对象的动作时,开灯=生成对象(allocnewcopymutableCopy),需要照明=持有对象(retain),不需要照明=释放对象(release),关灯=废弃对象(dealloc)。
引用计数的内存管理1.1 内存管理的原则自己生成的对象,自己持有也可持有非自己生成的对象释放不再需要自己持有的对象非自己持有的对象无法释放注意这些原则里的一些关键词与方法的对应关系:『生成』- allocnewcopymutableCopy『持有』- retain『释放』- release『废弃』- dealloc
下面分别来解释一下,这四条原则的含义:
1.1.1 自己生成的对象,自己持有id obj = [[NSObject alloc] init]; id obj = [NSObject new]; id obj = [NSObject copy]; id obj = [NSObject mutableCopy]; // 注意 allocnewcopymutableCopy 开头的驼峰式方法名,也生成并持有对象 id obj = [MyObject allocMyObj]; id obj = [MyObject newThatObj]; id obj = [MyObject copyThis]; id obj = [MyObject mutableCopyThat];
注意allocnewcopymutableCopy 开头非驼峰式命名的方法不适用上述规则。例如:allocatenewercopyingmutableCopyed。
1.1.2 非自己生成的对象,也可持有id obj = [NSMutableArray array];
obj是非『allocnewcopymutableCopy』或以其开头的驼峰式命名方法创建,因此属于非自己生成的对象。如何持有对象呢?用 retain 啊~
id obj = [NSMutableArray array];[obj retain];1.1.3 释放不再需要自己持有的对象
用allocnewcopymutableCopy』或以其开头的驼峰式命名方法生成并持有的对象,在不再需要的时候,要用 release 方法释放。
id obj = [[NSMutableArray alloc] init]; // do Something... [obj release];
id obj = [NSMutableArray array];[obj retain]; // do Something... [obj release];1.1.4 非自己持有的对象无法释放
释放非自己持有的对象时,会发生崩溃,例如
1)同一个对象被多次释放:
id obj = [[NSMutableArray alloc] init];[obj release];[obj release];
2)释放非自己持有的对象:
id obj = [obj0 object]; [obj release]; // obj 既不是 allocnewcopymutableCopy 出来的,也没有 retain,因此没有被持有,不可以被释放2. autorelease
有一个跟 release 类似的关键词autorelease,看这样一段代码:
- (id)object { id obj = [[NSMutableArray alloc] init]; [obj autorelease]; return obj;}
obj 对象在什么时候被释放呢?与 release 的区别是什么?
release和 autorelease 的区别对象被 release 时,引用计数-1,当引用计数为0时,该对象被立即释放。而对象被 autorelease 时,引用计数不变,该对象被注册到自动释放池中,在一个运行周期结束时,自动释放池被倾倒(池中注册的对象被 release)。
autorelease 类似 C 语言中的局部变量的特性,局部变量超过其作用域时会被自动废弃,autorelease 对待对象实例与之类似。当超出 autorelease 的作用域时,对象实例的 release 方法被调用。与 C 语言局部变量不同的是,autorelease 可以设置其作用域。
for(int i = 0;i < 10000; i ++){ @autoreleasepool { // 在一个 runloop 周期内产生大量对象的代码 }}
除了上述场景,总结一下需要显式调用 autoreleasepool 的情况:
显式使用@autoreleasepool:
autorelease 机制基于 UI framework,因此写 非UI framework的程序时,需要自己管理对象生存周期。
autorelease 触发时机发生在下一次runloop的时候。因此如何在一个大的循环里不断创建autorelease对象,那么这些对象在下一次runloop回来之前将没有机会被释放,可能会耗尽内存。这种情况下,可以在循环内部显式使用@autoreleasepool {}将autorelease 对象释放。
for (item in BigSet){ @autoreleasepool { //create large mem objects }}
自己创建的线程。Cocoa的应用都会维护自己autoreleasepool。因此,代码里spawn的线程,需要显式添加autoreleasepool。
很长的函数、很多中间变量时。正常情况下,你创建的变量会在超出其作用域的时候被释放掉。而如果函数写的很长,在函数运行过程中出现很多中间变量,占据了大量的内存,怎么办?用@autoreleasepool。在@autoreleasepool中创建的变量,会在@autoreleasepool结束的时候执行一次release,进行释放。其实@autoreleasepool就相当于一层作用域。
3. ARC的规则ARC 是从 iOS5出现的编译器新特性,对引用采取自动计数,不再需要手动的对对象进行 retain 和 release,编译器代替我们来做这件事了。
可以通过设置配置文件,在同一个项目中既有 ARC 也有 MRC(例如受老项目或老第三方库影响,需要在 ARC 项目中加入 MRC 的类)。
在 ARC 项目中用到 MRC:在targets的build phases选项下Compile Sources下选择要不使用arc编译的文件,双击,输入 -fno-objc-arc ; 在 MRC 中用到 ARC:同上步骤,选择要使用arc编译的文件,双击,输入 -fobjc-arc ;3.1 所有权修饰符ARC 同 MRC 一样,仍使用引用计数,仍适用1.1中内存管理的4条原则。ARC为何能自动释放呢?关键因素就是—— ARC 中增加了4种所有权修饰符:
__strong__weak__unsafe_unretained__autoreleasing其中,__strong__weak__autoreleasing对修饰的局部变量初始化为 nil。
以下着重介绍常用的__strong__weak修饰符.
3.1.1 __strong__strong 是 id 类型、对象类型的默认所有权修饰符,在 ARC 有效时不需要显式写出,如以下两行代码在 ARC 下是相同的:
id obj = [[NSObject alloc] init]; id __strong obj = [[NSObject alloc] init];
__strong 修饰符表示对对象强引用,在超过作用域时废弃,释放所引用的对象及其成员。相当于 MRC 中对该对象调用 release 方法。
__strong在 ARC中是如何实现 MRC的功能的?对比 MRC,MRC 通过手动写[obj release]来释放自己创建并持有的内存;
ARC 通过增加所有权修饰符这个概念,对 id|对象类型自己创建且持有的对象默认添加__strong 修饰符,从手动写 release 语句变为通过作用域控制对象及其成员的释放。
ARC 对非自己创建但持有的对象,也通过默认添加 __strong修饰符强引用,使其持有(相当于 MRC 中 retain 语句)对象。
回顾 MRC 内存管理的4条原则:
自己生成的对象,自己持有也可持有非自己生成的对象释放不再需要自己持有的对象非自己持有的对象无法释放『自己生成的对象,自己持有』『也可持有非自己生成的对象』,ARC 中对 id对象类型默认添加__strong 修饰符进行强引用;『释放不再需要自己持有的对象』变量作用域结束成员所属对象废弃对变量赋值都可以满足这条;『非自己持有的对象无法释放』ARC 中不再需要写 release 语句,因此这条也满足。因此 ARC 也是完全遵守 MRC 内存管理的原则的。
3.1.2 __weak为解决__strong 导致的循环引用问题,进而造成内存泄露(废弃的对象在超出其生存周期后继续存在),需要引入 _weak 修饰符,对造成循环引用的对象进行弱引用。当作用域结束时,被强引用的对象废弃,弱引用的对象自动被置为 nil。
3.1.3 __unsafe_unretainediOS4之前用,类似__weak(iOS 5),但需要在使用被其修饰的变量时,先判断是否存在。
3.1.4 __autoreleaseautorelease 修饰符同strong 一样一般不显式写出。在 MRC中,通过 NSAutoreleasePool 对象的声明和 drain 代码之间调用 autorelease 进行自动释放。在 ARC中,把需要自动释放的代码写在@autorelease{// code ...}中,当区间内非 allocnewcopymutableCopyinit 的对象会被自动加入 autoreleasepool,在作用域结束的时候释放。如下图:
MRCARC的自动释放对比5. ARC 的规则不能显式使用 retainreleaseretainCountautorelease不能使用 NSAllocateObjectNSDeallocateObject要遵守内存管理的方法命名规则(allocnewcopymutableCopy init)不显式调用 dealloc使用@autoreleasepool 替代 NSAutoreleasePool不使用 NSZone对象型变量不能作为 C 语言结构体成员显式转换 id 和 void*来源:简书
相关文章: