MRC下打破block引用循環(huán)的坑

感覺全世界都是在用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)確定類型了

解決方案:

  1. 將文件改為 ARC,因為ARC下,weak變量被釋放,會自動賦值nil就不會有問題了。
  2. 改用SDWebImage,因為SDWebImage在執(zhí)行block之前有判斷self是否為空,或者及時cancel請求就不會執(zhí)行completion block。YYWebImage在這點設(shè)計上有點缺陷,即使cancel了請求,也會執(zhí)行completion block,也沒有判斷self是否為空。
  3. 有些博客推薦是用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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容