block循環引用的問題:

在ARC模式下
執行下面語句:

1. - (IBAction)onTest:(id)sender  
2. {  
3.     BlockDemo *demo = [[BlockDemo alloc]init];  
4.     [demo setExecuteFinished:^{  
5.         if (demo.resultCode == 200) {  
6.             NSLog(@"call back ok.");  
7.         }  
8.     }];  
9.       
10.     [demo executeTest];  
11.        
12. }  

執行輸出結果:

1. 2015-07-24 19:20:33.997 blockDemo[25215:60b] Object Constructor!  
2. 2015-07-24 19:20:39.000 blockDemo[25215:60b] call back ok.  
一樣會被引入循環。

相信看到這裏的人,大多都要噴了,這哪一個不知道呀,還知道怎麼解決呢,非ARC中加了個__block,固然的在ARC中加一個__weak就搞定了。嗯,確實是這樣,但別急,接着往下看,絕對有收穫。在這裏先本身默認想一下,你是如何加這個__weak的。


對於第一個問是點block 的循環引用(retain cycle)到這裏暫告結束。下面講第二點。由於block告警在非ARC 中暫未發現因寫法引入(若是你知道,麻煩告訴我怎麼弄產生告警,我好研究一下。)
下面講在ARC模式下去除因寫法產生的告警時須要注意的問題。
像上面的寫法其實在ARC中會產生(Capturing 'demo' strongly in this block is likely to lead to a retain cycle)告警。以下圖:


在ARC中,編譯器智能化了,直接提示這樣寫會產生循環引用。所以不少愛去除告警的朋友就會想法去掉,好,咱再來看去掉時需注意的問題。
狀況一:

1. - (IBAction)onTest:(id)sender  
2. {  
3.     __weak BlockDemo *demo = [[BlockDemo alloc]init];  
4.     [demo setExecuteFinished:^{  
5.         if (demo.resultCode == 200) {  
6.             NSLog(@"call back ok.");  
7.         }  
8.     }];  
9.     [demo executeTest];  
10. }  
直接在前面加一個__weak,但這樣真的沒有告警了嗎?若是有,哪麼恭喜歡你,說明編譯器還幫你大忙。見下圖





這時還會告警,說這是一個WEAK變量,就立刻會被release。所以就不會執行block中的內容。你們能夠運行一下看
輸出結果爲:
1. 2014-07-24 19:38:02.453 blockDemo[25305:60b] Object Constructor!  
2. 2014-07-24 19:38:02.454 blockDemo[25305:60b] Object Destoryed!  
很顯然,立刻被release了,因此block 中的代碼根本就不執行。

謝天謝地,幸虧編譯器提早告訴了咱們有這個隱性危險。相信你們爲解決告警,又會獲得一個比較圓滿的解決方案,見下:
1. - (IBAction)onTest:(id)sender  
2. {  
3.     BlockDemo *demo = [[BlockDemo alloc]init];  
4.       
5.     __weak typeof(BlockDemo) *weakDemo = demo;  
6.       
7.     [demo setExecuteFinished:^{  
8.         if (weakDemo.resultCode == 200) {  
9.             NSLog(@"call back ok.");  
10.         }  
11.     }];  
12.     [demo executeTest];  
13. }  

這樣寫,即去除了告警又保證了block的運行。這纔是咱們最終想要的結果。
輸出爲:

1. 2014-07-24 19:40:33.204 blockDemo[25328:60b] Object Constructor!  
2. 2014-07-24 19:40:38.206 blockDemo[25328:60b] call back ok.  
3. 2014-07-24 19:40:38.207 blockDemo[25328:60b] Object Destoryed!  

但你們別得意。有提示,相信你們都能處理,並獲得個好的解決方法。哪麼下面大來再來看一下這個寫法,讓你真心甘拜下風。。。。。

1. - (IBAction)onTest:(id)sender  
2. {  
3.     __weak BlockDemo *demo = [BlockDemo blockdemo]; //這裏纔是重點,前面是[[BlockDemo alloc]init];會有告警。  
4.       
5.     [demo setExecuteFinished:^{  
6.         if (demo.resultCode == 200) {  
7.             NSLog(@"call back ok.");  
8.         }  
9.     }];  
10.     [demo executeTest];  
11. }  


其實只是把init放到了類方法中進行書寫而已,但會有什麼不一樣。

1. + (BlockDemo *)blockdemo  
2. {  
3.     return OBJC_AUTORELEASE([[BlockDemo alloc]init]);  
4. }  
不一樣點見下圖:真心看不到做何告警,是否是。但這存在什麼風險,風險就是運行的時候,block根本就沒有run。由於對象早就釋放了。


直接輸出:

1. 2015-07-24 19:47:53.033 blockDemo[25395:60b] Object Constructor!  
2. 2015-07-24 19:47:53.035 blockDemo[25395:60b] Object Destoryed!  

因 此,寫這個主要用來告戒一些喜歡用BLOCK但又想固然的朋友,有一些朋友喜歡去除告警,但只是盲目的加上__weak 或__block關鍵語,每每可能存在一些重大的安全隱患。就像演示中block根本不走。若是到了發佈時,爲了去告警而這樣簡單的處理了,並無進行測 試就打包。哪麼將死得很慘。。。。。


好,到了尾聲,來講說爲何朋友問我block會不會引行死循環,我說不會的理由。
見碼:

1. - (IBAction)onTest:(id)sender  
2. {  
3.     BlockDemo *demo = [BlockDemo blockdemo];//[[BlockDemo alloc]init];  
4.       
5.     [demo setExecuteFinishedParam:^(BlockDemo * ademo) {  
6.         if (ademo.resultCode == 200) {  
7.             NSLog(@"call back ok.");  
8.         }  
9.     }];  
10.       
11.     [demo executeTest];  
12. }  

不論是在外面init,仍是在裏面,且沒有加__block 及__weak。爲何,由於我我的經常在使用本身寫的block時,若是是回調,比較喜歡把自身看成參數傳到block中。這樣期實是編譯器給咱們作了弱引用。所以不會產生循環引用。

由 於我一直都這樣寫block,因此朋友一問起,我就說不會循環引用了,由於壓根他碰到的就是前面講述的哪一種訪問方式,而我回答的是個人這種使用方式。正因 爲口頭描述,與實際回覆真是差之千里。。。哈哈。爲了驗證我朋友的這個,我特地寫了個這篇文章,但願對你們有所幫助。最後,謝謝你們花時間閱讀。


安全

相關文章
相關標籤/搜索