揭开ARC的神秘面纱系列-第2话

博客地址

以下是正文:

写完第一篇关于揭开ARC神秘面纱的博客,我想和大家分享另外一些有趣的片段。这一次我好奇当你通过函数返回一个存在数组中的对象时会发生什么。非ARC模式,你可能会对这个对象retain一次再返回一个自动释放的对象。ARC模式下,我们虽然可以免去这些内存管理的操作,但还是不放心,觉得别扭。因此,我决定检测一下ARC是否做到位了。

考虑一下这个类:


    #import 
    
    @interface ClassA : NSObject
    @property (nonatomic, strong) NSMutableArray *array;
    @end
    
    @implementation ClassA
    
    @synthesize array;
    
    - (id)popObject {
        id lastObject = [array lastObject];
        if (lastObject) {
            [array removeLastObject];
        }
        return lastObject;
    }
    
    @end

在非ARC模式下,调用函数removeLastObject将会释放数组对对象的持有,如果这是对象的最后一个引用则对象的内存将会被释放,意味着返回的对象是一个已经被回收的对象。所以,我们应当retain一次lastObject并在返回前添加autorelease属性(加入自动释放池)。

尽管我完全明白ARC应该会完成这些工作,但是我还是担忧没有自己添加这些操作。我天真地以为ARC会一行行地解析函数中的代码。如果是这样,我觉得ARC也许没必要在我们引用lastObject对象的时候为它添加一次引用计数,此时ARC并不知道lastObject需要进行retain,所以ARC没必要非得做这些操作。

这就是我错误所在。显然,ARC在我们引用lastObject对象的时候为其添加一次引用计数,并在对象立刻作用域的时候进行了一次release操作,在我们这个例子中,由于我们是通过函数返回这个对象且函数名不是已关键字new或者copy开头,因此需要将对象加入自动释放池。

让我们看看上述代码编译之后的样子:


    .thumb_func     "-[ClassA popObject]"
    "-[ClassA popObject]":
        push    {r4, r5, r6, r7, lr}
        movw    r6, :lower16:(_OBJC_IVAR_$_ClassA.array-(LPC0_0+4))
        mov     r4, r0
        movt    r6, :upper16:(_OBJC_IVAR_$_ClassA.array-(LPC0_0+4))
        movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_1+4))
    LPC0_0:
        add     r6, pc
        movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_1+4))
    LPC0_1:
        add     r1, pc
        add     r7, sp, #12
        ldr     r0, [r6]
        ldr     r1, [r1]
        ldr     r0, [r4, r0]
        blx     _objc_msgSend
        @ InlineAsm Start
        mov     r7, r7          @ marker for objc_retainAutoreleaseReturnValue
        @ InlineAsm End
        blx     _objc_retainAutoreleasedReturnValue
        mov     r5, r0
        cbz     r5, LBB0_2
        movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
        movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC0_2+4))
        ldr     r0, [r6]
    LPC0_2:
        add     r1, pc
        ldr     r1, [r1]
        ldr     r0, [r4, r0]
        blx     _objc_msgSend
    LBB0_2:
        mov     r0, r5
        blx     _objc_autoreleaseReturnValue
        pop     {r4, r5, r6, r7, pc}

好吧,事实如此。ARC已经为我们考虑周全了。ARC在代码中插入了objc_retainAutoreleaseReturnValue调用,这意味着ARC已经觉察到需要给一个已经加入自动释放池的返回值增加引用计数,这个操作属于ARC的一种优化处理,它仅仅是把对象从自动释放池中移除而并非真的添加一次引用计数。接下来在函数结尾处,ARC调用了objc_autoreleaseReturnValue,这个函数将即将返回的对象加入自动释放池。

这仅仅是关于揭开ARC神秘面纱系列的另外一个例子。随着使用ARC的次数增多,我愈发意识它的实用性。ARC减少代码中内存管理相关的错误,并将上述的代码片段进行最佳优化处理。

你可能感兴趣的:(揭开ARC的神秘面纱系列-第2话)