iOS-ARC你看我就够了

作者: StrongX | 来源:发表于2015-09-18 23:32 被阅读4382次

    注:这一文章将是我自己的复习整理过程,仅供参考。
    (欢迎指出错误)

    ARC(Automatic Reference Counting),是一种对内存的管理技术。
    苹果的文档中是这么说的:

    在Objective-C中采用ARC机制,让编译器来进行内存管理。在新一代Apple LLVM 编译器中设置ARC为有效状态,就无需再次键入retain或者release代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减小了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅度提升。

    这些优点无疑极具吸引能力,但关于ARC技术,最重要的还是以下这一点:

    “在LLVM编译器中设置ARC为有效状态,就无需再次键入retian或者release代码。”

    C语言需要手动管理内存,这种痛苦用过的人应该都知道

    在OC中内存通过引用计数的方式进行管理。(ARC的本质就是自动替我们完成引用计数的部分)

    我们有必要粗略了解一下什么是引用计数。

    <code>
    当生成一个对象的时候,他就会有一个引用计数,那么这数字将会增大也可能会减小,当减小为0的时候,那么这个对象的内存块将会被释放。那么引用计数的关键就在于这个数字什么时候会增大什么时候会减小。
    </code>

    思考方式:

    • 自己生成的对象,自己持有。
    • 非自己生成的对象,自己也能持有。
    • 不再需要自己持有的对象时释放。
    • 非自己持有的对象无法释放。

    简单的说持有时引用计数+1,释放时引用计数-1,当引用计数为0的时候,则释放内存。
    本文讲叙述ARC情况下所引起的变化。


    我们首先必须要理解ARC中追加的所有权声明。
    ARC有效时所有类型都必须加上所有权修饰符。所有权修饰符一共有四种:

    • __strong 修饰符
    • __weak 修饰符
    • __unsafe unretained 修饰符
    • __autoreleasing 修饰符

    ** __strong 修饰符 **
    __strong修饰符是默认修饰符,表示对象的“强引用”,强引用对象在超出其作用域时将会被废弃,引用的对象释放。

    <pre>
    `
    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
     *
     */
    

    `
    </pre>

    通过上面这段代码希望大家能明白内存管理的思考方式。
    __strong修饰符能够对同一段内存进行持有,并共同管理。

    obj1释放时引用计数-1,这个时候只有obj2指向内存,因为是强引用,所以这个时候引用计数仍为1,所以内存并没有释放。

    • obj1和obj2的地位相同,也就是都能够对内存进行管理。

    view1和view2共同管理同一段内存,所以当view2修改以后,view1的值也会进行变化,因为指向同一段内存。

    __weak 修饰符

    然而strong修饰符并不能解决所有问题,当两个对象相互强引用对方的成员变量的时候,就会发生循环引用,循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生命周期后继续存在。那么在这个时候就引入了__weak修饰符,“弱引用”。
    因为带__weak修饰符的变量(即弱引用)不持有对象,所以在超出其作用域时,对象就会释放,所以因为强引用而造成的循环引用,将其中的成员变量改为弱引用,就不会发生相同情况。

    __weak修饰符还有另外一个优点。在持有某若引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值状态(空弱引用)。如以下代码所示。
    <pre>
    `
    NSObject __strong *obj1 = [[NSObject alloc]init];
    NSObject __weak *obj2 = obj1;

    obj1 = nil;
    /**
     *  obj1 = nil, obj2 = nil;
     *
     */
    

    `
    </pre>

    通过使用__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功能也是起作用的。
    以下两段代码是相同的:
    <pre>
    /*ARC无效*/ NSAutoreleasePool *pool = [[NS NSAutoreleasePool alloc] init]; id obj = [[NSObject alloc] init]; [obj autorelease]; [pool drain];
    </pre>
    <pre>
    /*ARC有效*/ @autoreleasepool { id __autoreleasing obj = [[NSObject alloc]init]; /** * obj 未释放 * */ } /** * obj已释放 * */
    </pre>

    指定“@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有效时属性与修饰符的对照关系:

    图片来自----额,我在书上拍的

    最后我们来看一下ARC中自动引用计数的数值究竟是多少

    我们来看这段代码:

    <pre>
    `
    {
    id __strong obj = [[NSObject alloc]init]; // retian count = 1;

        id __weak o = obj;                          // retian count = 1;
    }
    

    //retain count = 0;
    `
    </pre>

    和我们预期的一样,__strong修饰符使引用技术+1,而__weak修饰符,并不会使修饰符+1,早超出obj的作用域以后,引用技术-1,同时释放。

    我们再来看一下用__autoreleasing修饰符向autoreleasepool注册会怎么样:

    <pre>
    `
    @autoreleasepool {

        id __strong obj = [[NSObject alloc]init];   // retian count = 1;
        
        id __autoreleasing o = obj;                 // retian count = 2;
    }
    //retain count = 0;
    

    `
    </pre>

    __autoreleasing修饰符,使引用计数+1,而在超出autoreleasepool以后则清空并释放。

    最后再来看一下在autoreleasepool中使用__weak修饰符是什么样的:

    <pre>
    `
    @autoreleasepool {

        id __strong obj = [[NSObject alloc]init];   // retian count = 1;
        id __weak o = obj;                 // retian count = 2;
    }
    

    `
    </pre>

    在autoreleasepool中即使不使用autoreleasing修饰符,而用__weak修饰符替代,同样将obj对象注册到了autoreleasepool中。


    这篇文章大体上记叙ARC中自动计数的表现,其中包括一些我的理解,和参考书的摘抄。欢迎指出错误。

    如果觉得我写的不错,请关注我。

    相关文章

      网友评论

      • 小包包包:楼主,为哈 在 autoreleasepool中, id __strong obj = [[NSObject alloc]init]; // retian count = 1;
        id __weak o = obj; 引用计数变成2了呀,weak不是弱引用吗?为什么会变成2?
      • 小码僧:你好“和我们预期的一样,strong修饰符使引用技术+1,而weak修饰符,并不会使修饰符+1,早超出obj的作用域以后,引用技术-1,同时释放。”,我测试的时候,weak修饰符,并真的会使修饰符+1,即时在autorelease pool外面。这是什么回事?您写错了吗
      • 李连毛:博主是不是看过OC高级编程。
      • 22d4ed3d1a0a:想请教一个问题,如果使用异步的网络请求,请求的闭包里调用了`self.anotherFunction() ->anotherClosure(var: VarClass -> Void)`(闭包嵌套另一个闭包),这样的话引用计数怎样计算呢?如果在外层闭包写了`[weak self]`,在内层循环if let sSelf = self 则会返回nil。求指教。
      • 95a63e375514:为什么鲁能失踪nszone,初始化单例就用到了
      • YotrolZ:强引用、超出作用域不是不会释放吗?
        StrongX:@YotrolZ 超出作用域以后 内存块的引用计数-1,这个时候是否释放取决于引用-1以后是否为0,也就是是否还有其他强引用指针指向这块内存块
      • 4b5adddc15ae:那本书是不是封面有个狮子的,白色背景书皮。
        如果是,这本书有问题,翻译的中文语言组织十分草蛋,词不达意,各种隐晦各种不知道说什么,配图什么的都不符合逻辑等等。
        英贰与我:@StrongX 好像就是那本书
        StrongX:@FBO 名字好像叫iOS 内存管理 在中文书里头已经算不错了。
      • 7eb87033b5d6:列出的很详细,可是有好多点没有点到。比如,arc下的野指针,内存泄露,arc下和mrc下的block
        StrongX:@Littledogboy 可以再写一篇block哈。
      • 不要关注我好吗:太有深度了,oc只看过代码,没看过文章。
      • 546055050dc0:思路很清晰,赞!
        StrongX:@EXALEX 谢谢

      本文标题:iOS-ARC你看我就够了

      本文链接:https://www.haomeiwen.com/subject/qbzpcttx.html