ARC(Automatic Reference Counting),是一种对内存的管理技术。 苹果的文档中是这么说的: > 在Objective-C中采用ARC机制,让编译器来进行内存管理。在新一代Apple LLVM 编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减小了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅度提升。 这些优点无疑极具吸引能力,但关于ARC技术,最重要的还是以下这一点: **“在LLVM编译器中设置ARC为有效状态,就无需再次键入retian或者release代码。”** >C语言需要手动管理内存,这种痛苦用过的人应该都知道 在OC中内存通过引用计数的方式进行管理。(ARC的本质就是自动替我们完成引用计数的部分) 我们有必要粗略了解一下什么是引用计数。 ``` 当生成一个对象的时候,他就会有一个引用计数,那么这数字将会增大也可能会减小,当减小为0的时候,那么这个对象的内存块将会被释放。那么引用计数的关键就在于这个数字什么时候会增大什么时候会减小。 ``` 思考方式: > - 自己生成的对象,自己持有。 - 非自己生成的对象,自己也能持有。 - 不再需要自己持有的对象时释放。 - 非自己持有的对象无法释放。 简单的说持有时引用计数+1,释放时引用计数-1,当引用计数为0的时候,则释放内存。 本文讲叙述ARC情况下所引起的变化。 ------------------------- 我们首先必须要理解ARC中追加的所有权声明。 ARC有效时所有类型都必须加上所有权修饰符。所有权修饰符一共有四种: - __strong 修饰符 - __weak 修饰符 - __unsafe unretained 修饰符 - __autoreleasing 修饰符 # __strong 修饰符 __strong修饰符是默认修饰符,表示对象的“强引用”,强引用对象在超出其作用域时将会被废弃,引用的对象释放。 ``` id __strong obj1 = [[NSObject alloc]init]; id __strong obj2 = obj1; obj1 = nil; /** * obj1 = nil, obj2 != nil * */ UIView *view1 = [[UIView alloc]init]; UIView *view2 = view1; view1.alpha = 0.4; view2.alpha = 0.5; /** * view1.alpha = 0.5, view2.alpha = 0.5 * */ ``` 通过上面这段代码希望大家能明白内存管理的思考方式。 __strong修饰符能够对同一段内存进行持有,并共同管理。 > obj1释放时引用计数-1,这个时候只有obj2指向内存,因为是强引用,所以这个时候引用计数仍为1,所以内存并没有释放。 - obj1和obj2的地位相同,也就是都能够对内存进行管理。 view1和view2共同管理同一段内存,所以当view2修改以后,view1的值也会进行变化,因为指向同一段内存。 # __weak 修饰符 # 然而strong修饰符并不能解决所有问题,当两个对象相互强引用对方的成员变量的时候,就会发生循环引用,循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生命周期后继续存在。那么在这个时候就引入了__weak修饰符,“弱引用”。 因为带__weak修饰符的变量(即弱引用)不持有对象,所以在超出其作用域时,对象就会释放,所以因为强引用而造成的循环引用,将其中的成员变量改为弱引用,就不会发生相同情况。 __weak修饰符还有另外一个优点。在持有某若引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值状态(空弱引用)。如以下代码所示。 ``` NSObject __strong *obj1 = [[NSObject alloc]init]; NSObject __weak *obj2 = obj1; obj1 = nil; /** * obj1 = nil, obj2 = nil; * */ ``` 通过使用__weak修饰符可避免循环引用。通过检查附有__weak修饰符的变量是否为nil,可以判断被赋值对象是否已废弃。 遗憾的是,__weak修饰符只能用于iOS5以上及OS X Lion以上版本的应用程序。在iOS4以及OS X Snow Leopard的应用程序中可使用 __unsafe unretained修饰符来代替。 # __unsafe unretained 修饰符 __unsafe unretained与weak修饰符一样不会增加引用计数,自己生成的对象不能继续为自己所有,所以会立即释放。 那么__unsafe unretained修饰符与weak修饰符有什么区别呢? > 比如在iOS4以及OS X Snow Leopard的应用程序中,必须使用__unsafe unretained修饰符来替代__weak修饰符。赋值给附有__unsafe unretained修饰符变量的对象在通过该变量使用时,如果没有确保其存在,那么应用就会崩溃。 # __autoreleasing 修饰符 ARC有效时不能使用autorelease方法,同时不能使用NSAutoreleasePool类。但是,事实上ARC有效时auto lease功能也是起作用的。 以下两段代码是相同的: ``` /*ARC无效*/ NSAutoreleasePool *pool = [[NS NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain]; ``` ``` /*ARC有效*/ @autoreleasepool { id __autoreleasing obj = [[NSObject alloc]init]; /** * obj 未释放 * */ } /** * obj已释放 * */ ``` 指定**“@autoreleasepool块”**来替代**“NSAutoreleasePool类对象生成、持有以及废弃”**这一范围。 另外,ARC有效时,要通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法。对象赋值给附有__autoreleasing修饰符的变量等价于在ARC有效时调用对象的autorelease方法,即对象被注册到autoreleasepool。 也就是可以说ARC有效时,用@aotureleasepool块替代NSAutoreleasePool类,用附有__autoreleasing修饰符的变量替代autorelease方法。 > 因为autoreleasepool范围以块级源代码表示,提高了程序的可读性,所以今后在ARC无效时也推荐使用@autoreleaseepool块。 另外,无论ARC是否有效,调试用的非公开函数_objc_autoreleasePoolPrint()都可使用。 _objc_rootRetainCount(obj) 利用这一函数可有效的帮助我们调试注册到autoreleasepool上的对象。 -------------------------- 上面讲解了四种修饰符,在ARC有效的情况下,必须遵守一定的规则。下面就是具体的ARC规则: >- 不能使用retain/release/retainCount/autorelease - 不能使用NSAllocateObject/NSDeallocateObject - 必须遵守内存管理的方法命名规则 - 不能显示的调用dealloc - 使用@autoreleasepool块来替代NSAutoreleasePool - 不能使用区域(NSZone) - 对象型变量不能作为C语言结构体(struct/union)的成员 - 显示的转化“id”和“void*” -------- 我们再来看一下ARC有效时属性与修饰符的对照关系: ![图片来自----额,我在书上拍的](/static/images/essay/12.jpeg) --------------- **最后我们来看一下ARC中自动引用计数的数值究竟是多少** 我们来看这段代码: ``` { id __strong obj = [[NSObject alloc]init]; // retian count = 1; id __weak o = obj; // retian count = 1; } //retain count = 0; ``` > 和我们预期的一样,__strong修饰符使引用技术+1,而__weak修饰符,并不会使修饰符+1,早超出obj的作用域以后,引用技术-1,同时释放。 我们再来看一下用__autoreleasing修饰符向autoreleasepool注册会怎么样: ``` @autoreleasepool { id __strong obj = [[NSObject alloc]init]; // retian count = 1; id __autoreleasing o = obj; // retian count = 2; } //retain count = 0; ``` > __autoreleasing修饰符,使引用计数+1,而在超出autoreleasepool以后则清空并释放。 最后再来看一下在autoreleasepool中使用__weak修饰符是什么样的: ``` @autoreleasepool { id __strong obj = [[NSObject alloc]init]; // retian count = 1; id __weak o = obj; // retian count = 2; } ``` > 在autoreleasepool中即使不使用autoreleasing修饰符,而用__weak修饰符替代,同样将obj对象注册到了autoreleasepool中。 ### 欢迎加入iOS交流群537774852 --------------------------