你真的會用strong weak dance嗎?

2021-09-11 13:27:50 字數 4627 閱讀 6464

下文的討論基於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...