一. 引用计数式内存管理的思考方式
- 自己生成的对象自己持有(以下是常用的初始化方法)
- alloc
- new
- copy (生成并持有不可变的对象)
- mutableCopy (生成并持有可变的对象)
- 非自己生成的对象,自己也能持有
- array (使用array初始化的时候,取得的对象不是自己所持有的,需要持有的时候,需要使用retain方法)
通过retain方法,非自己生成的对象跟用第一点的方法生成并持有的对象一样,成为了自己所持有的。
- 不再需要自己持有对象时释放
- release -- 使用该方法可以释放通过第一点的方法或者retain方法持有的对象。
- autorelease -- 使用该方法,可以使取得的对象存在,但自己不持有对象。autorelease方法提供的功能:使对象在超出指定的生存范围时能够自动并正确地释放。
- 无法释放非自己持有的对象
释放非自己持有的对象,会导致应用程序崩溃!!!
二. 所有权修饰符
Objective-C为了处理对象,可将变量类型定义为id类型或各种对象类型。所谓对象类型就是指向NSObject这样的Objective-C类的指针,id类型则用于隐藏对象类型的类名部分。ARC有效的时候,id类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。所有权修饰符一共有4种。
-
__strong 修饰符
- __strong修饰符是id类型和对象类型默认的所有权修饰符。
id obj = [[NSObject alloc] init]; id __strong obj = [[NSObject alloc] init]; // 两者是相同的像这样初始化的时候,id变量已被附加了所有权修饰符。 __strong修饰符表示对对象的“强引用”。持有者引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。
- 自己生成并持有对象的情况
{ // 自己生成并持有对象 id __strong obj = [[NSObject alloc] init]; // 因为变量obj为强引用,所以自己持有对象 } // 变量obj超出其作用域,强引用失效,释放自己持有的对象- 取得非自己生成并持有对象的情况
{ // 取得非自己生成并持有对象 id __strong obj = [NSMutableArray array]; // 因为变量obj为强引用,所以自己持有对象 } // 变量obj超出其作用域,强引用失效,释放自己持有的对象- 被__strong修饰的变量之间可以相互赋值
思考一下:
id __strong obj0 = [[NSObject alloc] init]; // 对象A id __strong obj1 = [[NSObject alloc] init]; // 对象B id __strong obj2 = nil; obj0 = obj1; obj2 = obj0; // 问题:对象A和对象B分别被谁持有? obj0 = nil; obj1 = nil; obj2 = nil; // 问题:对象A和对象B分别被谁持有?过程解答:
id __strong obj0 = [[NSObject alloc] init]; // 对象A /* * obj0持有对象A的强引用 */ id __strong obj1 = [[NSObject alloc] init]; // 对象B /* * obj1持有对象B的强引用 */ id __strong obj2 = nil; /* * obj2不持有任何对象 */ obj0 = obj1; /* * obj0持有由obj1赋值的对象B的强引用 * 因为obj0被赋值,所以的对对象A的强引用失效 * 对象A的所有者不存在,因此废弃对象A */ obj2 = obj0; /* * obj2持有由obj0赋值的对象B的强引用 */ /* * 答案:对象A被废弃 * 对象B被obj0、obj1、obj2持有 */ obj0 = nil; /* * 因为nil被赋值了obj0,所以对对象B的强引用失效 */ obj1 = nil; /* * 因为nil被赋值了obj1,所以对对象B的强引用失效 */ obj2 = nil; /* * 因为nil被赋值了obj2,所以对对象B的强引用失效 * 对象B的所有者不存在,因此废弃对象B */ /* * 答案:对象A被废弃 * 对象B被废弃 */经过上面的例子,不难发现:__strong修饰符的变量,不仅只在变量作用域中,在赋值上也能够正确地管理其对象的所有者。
-
__weak 修饰符
看到这个修饰符,必然就会想到“循环引用”,循环引用是开发中很常见的问题。 常见的循环引用:互相强引用、对自身强引用
- 互相强引用
{ id test0 = [[Test alloc] init]; // 对象A /* * test0持有Test对象A的强引用 */ id test1 = [[Test alloc] init]; // 对象B /* * test1持有Test对象B的强引用 */ [test0 setObject:test1]; /* * Test对象A的obj_成员变量持有Test对象B的强引用。 * 此时,持有Test对象B的强引用的变量为Test对象A的obj_和test1。 */ [test1 setObject:test0]; /* * Test对象B的obj_成员变量持有Test对象A的强引用。 * 此时,持有Test对象A的强引用的变量为Test对象B的obj_和test0。 */ } /* * 因为test0变量超出其作用域,强引用失效,所以自动释放Test对象A。 * 因为test1变量超出其作用域,强引用失效,所以自动释放Test对象B。 * 此时持有Test对象A的强引用的变量为Test对象B的obj_。 * 此时持有Test对象B的强引用的变量为Test对象A的obj_。 * 因此,发生了内存泄漏。 */
内存泄漏:应当废弃的对象在超出其生存周期后继续存在。
- 对自身的强引用
id test = [[Test alloc] init]; [test setObject:test];
这时候,__weak修饰符就有作用了,__weak修饰符与__strong修饰符相反,提供弱引用,弱引用不能持有实例对象。同时,在持有某对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态,我们来看一下示例代码:
id __weak obj0 = nil; { id __strong obj1 = [[NSObject alloc] init]; obj0 = obj1; NSLog(@"A: %@", obj0); } NSLog(@"B: %@", obj0);该示例代码的运行结果:
A: <NSObject: 0x753e180> B: (null)下面来分析一下对象的持有情况:
id __weak obj0 = nil; { id __strong obj1 = [[NSObject alloc] init]; /* * 自己生成并持有对象 * 因为obj1变量为强引用, * 所以自己持有对象 */ obj0 = obj1; /* * obj0变量持有对象的弱引用 */ NSLog(@"A: %@", obj0); /* * 输出obj0变量持有的弱引用的对象 */ } /* * 因为obj1变量超出其作用域,强引用失效,所以自动释放自己持有的对象 * 因为对象无持有者,所以废弃该对象 * 废弃对象的同时,持有该对象的弱引用的obj0变量的弱引用失效,nil赋值给obj0 */ NSLog(@"B: %@", obj0); /* * 输出赋值给obj0变量中的nil */通过这个示例代码可以知道:使用__weak修饰符可避免循环引用。通过检查附有__weak修饰符的变量是否为nil,可以判断被赋值的对象是否已废弃。
-
__unsafe_unretained 修饰符
- __unsafe_unretained修饰符是不安全的修饰符,尽管ARC式的内存管理是编译器的工作,但被__unsafe_unretained修饰的变量不属于编译器的内存管理对象。
- 和被__weak修饰的变量一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会被立即释放。
- 在使用__unsafe_unretained修饰符的时候,赋值给被__strong修饰的变量的时候有必要确保被赋值的对象确实存在。
-
__autoreleasing 修饰符 ARC有效的时候不能使用autorelease方法,也不能使用NSAutoreleasePool类。虽然autorelease不能直接使用,但在ARC有效的时候是起作用的。
- ARC无效时的autorelease
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; - ARC有效时
@autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init]; }
显然,在ARC里,用"@autoreleasepool块"来替代"NSAutoreleasePool"类对象生成、持有以及废弃。
那么,__autoreleasing 修饰符扮演的是一个什么角色呢?
对象赋值给被__autoreleasing修饰的变量时,相当于在ARC无效的时候调用对象的autorelease方法,即对象被注册到autoreleasepool。
- ARC无效时的autorelease
ps: 本文是作者看书的时候,为了方便记忆以及复习所记录下来的,很多地方引用了书中的例子,望各位路过的大佬不要介意。同时,也希望各位路过的大佬有所收获,新年快乐。