Dark Corner In ARC

b10l07發表於2018-12-21

自從有了ARC,iOSer感覺得到了解放,再也不用考慮記憶體問題了,最多注意一下迴圈引用

iOS系統Framework很多地方對引數處理不統一。比如NSTimer強引用target,NSNotificationCenter是unsafe_assign,NSURLSession的delegate是retain等等

ARC通過分析程式碼,在合適的位置幫我們做記憶體管理。但是動態呼叫,就不能那麼智慧了。比如下面程式碼就會導致洩漏

- (void)test {
    for (int i = 0; i < 4; i++) {
        SEL sel = NSSelectorFromString(@"newData");
        [self performSelector:sel];
    }
    
    return YES;
}

- (MYData *)newData {
    
    MYData *data = [MYData new];
    return data;
}

可以看一下newData的彙編程式碼(Product->Perform Action->Assembly)

    .p2align    2
Lfunc_begin3:
    .loc    17 62 0                
    .cfi_startproc
    adrp    x8, l_OBJC_CLASSLIST_REFERENCES_$_.35@PAGE
    ldr x0, [x8, l_OBJC_CLASSLIST_REFERENCES_$_.35@PAGEOFF]
    adrp    x8, l_OBJC_SELECTOR_REFERENCES_.37@PAGE
    ldr x1, [x8, l_OBJC_SELECTOR_REFERENCES_.37@PAGEOFF]
    b   _objc_msgSend
Lfunc_end3:
    .cfi_endproc

ARC沒有對返回的物件release,直接返回了。performSelector由於不知道返回的物件是什麼,也不會它做任何操作,於是乎這個物件就洩漏了。

如果把newData改為getData,看看彙編程式碼有什麼不同

    .p2align    2               ;
Lfunc_begin4:
    .cfi_startproc
    stp x29, x30, [sp, #-16]!   ; 8-byte Folded Spill
    mov x29, sp
    adrp    x8, l_OBJC_CLASSLIST_REFERENCES_$_.35@PAGE
    ldr x0, [x8, l_OBJC_CLASSLIST_REFERENCES_$_.35@PAGEOFF]
    adrp    x8, l_OBJC_SELECTOR_REFERENCES_.37@PAGE
    ldr x1, [x8, l_OBJC_SELECTOR_REFERENCES_.37@PAGEOFF]
    bl  _objc_msgSend
    ldp x29, x30, [sp], #16     ; 8-byte Folded Reload
    b   _objc_autoreleaseReturnValue
Lfunc_end4:
    .cfi_endproc

多了一個_objc_autoreleaseReturnValue呼叫。正是這個ARC插入的呼叫,將物件放入自動釋放池,就不會出現洩漏。

OC有一套自己的命名規則,已new、copy,create開頭的方法,預設都是呼叫方負責釋放物件。所以,performSelector還是可以用的,就是要注意不要呼叫上面這種方法就可以了(官方建議使用NSInvocation代替)。

相關文章