下文的討論基於arc平時開發中我們遇到block裡面引用self的情況,大部分都是這樣處理的
__weak
typeof(self) weakself = self;
self.myblock = ^;
複製**
我們習慣了這樣用,**貌似這樣用了之後可以解決迴圈引用的問題,而且可以保證block執行之前self不會被釋放掉?真相總是殘酷的,然而事實並非如此!**下面將會對block中引用self的三種方式進行討論,並給出原因和另外一種解決方案。
這種情況使用是block被沒有被self強引用,因此這樣不會導致retain cycle。
dispatch_block_t completionhandler = ^
複製**
當block被self強引用,此時如果在block內強引用self將會導致retain cycle。所以我們就想到了在block外部建立乙個weakself,然後block在建立的時候捕獲到的是weakself,這樣就不會導致retain cycle。
__weak
typeof(self) weakself = self;
dispatch_block_t block = ^;
複製**
但是要注意的是block捕獲的是weakself變數,如果在執行dosomething的過程中self被釋放掉,由於是弱引用,weakself也將置空,下面的dosomethingelse是無法得到執行的,看乙個例子:
下面的例子展示的是,在block呼叫之後的1秒後釋放self,在block中呼叫dosomething,2秒之後再呼叫doanotherthing,意味著呼叫doanotherthing之前self已經被釋放了
- (void)viewdidload );
}-(void)test
); }];
self.myobject.weakblock();
}-(void)dosomething
-(void)doanotherthing
-(void)dealloc
複製**
從列印日誌可以看出,block執行大約1秒之後self被dealloc,doanotherthing並沒有得到呼叫
2017
-01-16
14:31:13.834
strong-weak dance[11366:4727954] 呼叫block!
2017
-01-16
14:31:13.836
strong-weak dance[11366:4727954] -[strongweakself dosomething]
2017
-01-16
14:31:14.893
strong-weak dance[11366:4727954] self.block被釋放!
2017
-01-16
14:31:14.893
strong-weak dance[11366:4727954] -[strongweakself dealloc]
複製**
所以只使用weakself,在self被釋放之後,weakself由於self的釋放已經為空,後面的self都將失效,所以在block中這樣引用self是非常危險的,下面就要談談我們最熟悉的strong-weak dance了。
對比第二種方案我們看一下doanotherthing是否可以得到呼叫,稍微改一下**,還是在block執行1秒後釋放self,我們看看後面的self引用是否有效
-(void)test
); }];
self.myobject.weakblock();
}複製**
此時看列印日誌:
2017
-01-16
14:36:39.039
strong-weak dance[11374:4728878] 呼叫block!
2017
-01-16
14:36:39.039
strong-weak dance[11374:4728878] -[strongweakself dosomething]
2017
-01-16
14:36:40.110
strong-weak dance[11374:4728878] self.block被釋放!
2017
-01-16
14:36:41.213
strong-weak dance[11374:4728878] -[strongweakself doanotherthing]
2017
-01-16
14:36:41.213
strong-weak dance[11374:4728878] -[strongweakself dealloc]
複製**
雖然self被釋放掉了,但是並沒有dealloc,因為block內部的strongself對他進行了一次retain,當doanotherthing執行完畢,strongself對他的引用計數減一,self被dealloc徹底銷毀。
**那麼問題來了!strong-weak dance能不能解決block執行前,self被釋放的問題?**下面繼續驗證
我們改一下**,在1秒之後釋放self,在2秒之後執行block(注意延時block中對於self的處理是weakself,防止延時block對self進行retain影響驗證結果)
- (void)viewdidload );
}-(void)test
); }];
dispatch_after(dispatch_time(dispatch_time_now, (int64_t)(2 * nsec_per_sec)), dispatch_get_main_queue(), ^);
}複製**
看一下日誌:
2017
-01-16
14:44:26.314
strong-weak dance[11395:4730727] self.block被釋放!
2017
-01-16
14:44:26.314
strong-weak dance[11395:4730727] -[strongweakself dealloc]
2017
-01-16
14:44:27.372
strong-weak dance[11395:4730727] (null)
複製**
當開始呼叫block的時候報錯了,self這時已經被dealloc掉。strong-weak dance並沒有解決這種問題。看到這心是不是涼了半截?真相就是如此,我們平時一直使用的strong-weak dance也只能解決block得到呼叫之後self不被釋放的問題。
這是我們最常用的是一種方案,因為block建立時捕獲的是weakself,所以block執行之前不能夠控制self的生命週期,所以這樣不會導致整個block對self進行強引用。之後在block內部建立乙個對self進行retain的變數strongself,strongself 作為區域性變數強引用了 self 並且會在block執行完畢的時候被自動銷毀,這樣既可以保證在block執行期間 self 不會被外界乾掉,同時也解決了retain cycle的問題。
通過上面幾個小栗子可以看出來:strong-weak dance確實是比較好的解決方案,但是也不是萬能的,他不能解決block呼叫之前self被釋放的問題,下面將block中引用self分為4中場景:
1. 使用self
當self不持有、不間接持有block時,可以在block內部直接引用self。
2.使用weakself
當self持有或間接持有block,可以通過在外部建立self的弱引用weakself然後捕獲到block內部進行使用,但是這樣使用存在一定風險,一般也不推薦使用。
3.使用strong-weak dance
當self持有或間接持有block,此時要使用strong-weak dance。 這種方法也不是萬能的,在block被執行前,block對self依然只是弱引用,進入block裡面才會retain一次,保證在block執行期間self都不會被釋放掉。
4. block中強引用self並且打破retain cycle
不管是weakself還是strong-weak dance,目的都是避免retain cycle,strong-weak dance的本質也是在block中搞了乙個區域性變數來打破這種迴圈引用的;
如果我們在block中直接使用self,並且在適當的時機打破這種迴圈(比如說在block執行完成將這個block銷毀)也可以避免retain cycle,並且這種在block建立時就強引用的方式,在block被呼叫前 self 不會被釋放掉,可以彌補strong-weak dance的不足。
你真的會用GOOGLE嗎
平時很多人用google搜尋引擎搜尋資訊,經常搜尋 成千上萬的網頁,檢視幾頁就沒耐心找下去了。在google上搜尋資訊,不只輸入希望搜尋的片語這麼簡單,這樣是無法得到做好的搜尋結果的。google 為使用者提供了很多基本搜尋語法,熟練的運用google搜尋,將很快的搜到我們需要的結果。短語搜尋 布林...
Markdown 你真的會用嗎?
markdown指南 markdown basics 我有幾張阿里雲幸運券分享給你,用券購買或者公升級阿里雲相應產品會有特惠驚喜哦!把想要買的產品的幸運券都領走吧!快下手,馬上就要搶光了。我常用如 橫線分隔符 超級鏈結 markdown 常遇到的問題 縮排 每段文章我都會習慣性地做首行縮排,但如果直...
你真的會用scanf嗎?
scanf使用技巧1 456 7894 54如何將以上數字輸入陣列呢,只需要用下面這段 scanf d arr i 實際上在鍵盤上輸入時,鍵入4 空格 5 空格 6 空格 scanf使用技巧2 當我們有有乙個很大的陣列需要輸入時,而每次輸入元素的個數都不一樣,這時該怎麼辦呢?使用下面這段 即可 in...