感覺全世界都是在用ARC,而我還在MRC里掙扎。攤手.jpg
先看看這個crash
重現(xiàn)步驟是制造弱網(wǎng)絡(luò)環(huán)境,清除圖片緩存,在cell的圖片下載完畢之前,pop掉這個vc。demo

屏幕快照 2017-07-15 下午2.51.04.png
- 使用
__block(MRC) 和__weak(ARC) 修飾變量,來防止block對變量retain,從而避免引用循環(huán); - 在block內(nèi)部聲明一個
__strong修飾的變量來防止,變量在block執(zhí)行過程中被釋放。
這兩個技巧想必大家都已經(jīng)很熟悉了。
然而上圖的crash是怎么發(fā)生的呢?
很明顯cell變成野指針了,從而導(dǎo)致了crash,而且cell的釋放是發(fā)生在執(zhí)行block之前,也就是說執(zhí)行block的時候block_cell就已經(jīng)是野指針了,再怎么用__strong修飾都是沒用的。
- 圖中代碼不寫
__strong,因為__strong是默認(rèn)的修飾符。 -
__typeof__(cell)不會導(dǎo)致retain cell,是因為__typeof__()在編譯期就已經(jīng)確定類型了
解決方案:
- 將文件改為 ARC,因為ARC下,weak變量被釋放,會自動賦值nil就不會有問題了。
- 改用SDWebImage,因為SDWebImage在執(zhí)行block之前有判斷self是否為空,或者及時cancel請求就不會執(zhí)行completion block。YYWebImage在這點設(shè)計上有點缺陷,即使cancel了請求,也會執(zhí)行completion block,也沒有判斷self是否為空。
- 有些博客推薦是用malloc_zone_from_ptr在MRC下判斷指針是否已分配。我的測試結(jié)果是不可行的,因為這個指針有可能被釋放后,又重新分配了,它還是一個野指針。
總結(jié):
在MRC下使用 @weakify @strongify 等等 Weak-Strong Dance技巧還是需要考慮對象、block的生命周期、內(nèi)存模型等等。
附上官方文檔中 在ARC下解決引用循環(huán)的三個列子
你可以使用__block修飾詞,然后就能在completion handler把myController變量置為nil
MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};
或者你也可以使用weak變量,下面是一個簡單的效果例子:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyViewController = myController;
myController.completionHandler = ^(NSInteger result) {
[weakMyViewController dismissViewControllerAnimated:YES
completion:nil];
};
為了解決non-trivial cycles,你可以這樣使用:
MyViewController *myController = [[MyViewController alloc] init…];
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else {
// Probably nothing...
}
};
在某些場合,我們還可以對于那些無法匹配weak字段的類使用__unsafe_unretained 字段,然而它在實際中仍然無法解決non-trivial cycles問題,因為它很難甚至不可能來保持__unsafe_unretained指針活躍并且仍然指向同一個對象。
參考:
關(guān)于循環(huán)引用
使用block注意事項
蘋果文檔
weak-strong dance 討論
嵌套block
深入研究Block