日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

对 Strong-Weak Dance的思考

發布時間:2023/12/20 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 对 Strong-Weak Dance的思考 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在使用 Block 時,除了使用 __weak 修飾符避免循環引用外,還有一點經常容易忘記。蘋果把它稱為:“Strong-Weak Dance”。

問題來源

這是一種 強引用 --> 弱引用 --> 強引用 的變換過程。在弄明白為什么要如此大費周章之前,我們首先來看看一般的寫法會有什么問題。

__weak MyViewController *wself = self; self.completionHandler = ^(NSInteger result) { [wself.property removeObserver: wself forKeyPath:@"pathName"]; }; 復制代碼

這種寫法可以避免循環引用,但是我們要考慮這樣的問題:

假設 block 被放在子線程中執行,而且執行過程中 self 在主線程被釋放了。由于 wself 是一個弱引用,因此會自動變為 nil。而在 KVO 中,這會導致崩潰。

Strong-Weak Dance

解決以上問題的方法很簡單,新增一行代碼即可:

__weak MyViewController *wself = self; self.completionHandler = ^(NSInteger result) { __strong __typeof(wself) sself = wself; // 強引用一次 [sself.property removeObserver: sself forKeyPath:@"pathName"]; }; 復制代碼

這樣一來,self 所指向對象的引用計數變成 2,即使主線程中的 self 因為超出作用于而釋放,對象的引用計數依然為 1,避免了對象的銷毀。

思考

在和小伙伴的討論過程中,他提出了幾個問題。雖然都不難,但是有利于把各種知識融會貫通起來。

  • Q:下面這行代碼,將一個弱引用的指針賦值給強引用的指針,可以起到強引用效果么?
  • __strong __typeof(wself) sself = wself; 復制代碼

    A:會的。引用計數描述的是對象而不是指針。這句話的意思是:

    sself 強引用 wself 指向的那個對象

    因此對象的引用計數會增加一個。

  • Q:block 內部定義了sself,會不會因此強引用了 sself?
  • A:不會。block 只有截獲外部變量時,才會引用它。如果是內部新建一個,則沒有任何問題。

  • Q:如果在 block 內部沒有強引用,而是通過 if 判斷,是不是也可以,比如這樣寫:
  • __weak MyViewController *wself = self; wself.completionHandler = ^(NSInteger result) { if (wself) { // 只有當 wself 不為 nil 時,才執行以下代碼 [wself.property removeObserver: wself forKeyPath:@"pathName"]; } }; 復制代碼

    A:不可以!考慮到多線程執行,也許在判斷的時候,self 還沒釋放,但是執行 self 里面的代碼時,就剛好釋放了。

  • Q:那按照這個說法,block 內部強引用也沒用啊。也許 block 執行以前,self 就釋放了。
  • A:有用!如果在 block 執行以前,self 就釋放了,那么 block 的引用計數降為 0,所以自己就會被釋放。這樣它根本就不會被執行。另外,如果執行一個為 nil 的閉包會導致崩潰。

  • Q:如果在執行 block 的過程中,block 被釋放了怎么辦?
  • A:簡單來說,block 還會繼續執行,但是它捕獲的指針會具有不確定的值,詳細內容請參考這篇文章

    @strongify 和 @weakify

    這是 ReactiveCocoa 中定義的一個宏。一般可以這樣使用:

    @weakify(self); self.completionHandler = ^(NSInteger result) { @strongify(self); [self.property removeObserver: sself forKeyPath:@"pathName"]; }; 復制代碼

    本文并非分析它們的實現原理,所以就簡單解釋兩點:

  • 這里的“@”沒有任何用處,僅表示強調,這個宏實際上包含了一個空的 AutoreleasePool,這也就是為什么一定要加上“@”。

  • 它的原理還是和之前一樣,生成了一段形如 __weak MyViewController *wself = self; 這種格式的代碼:

  • #define rac_strongify_(INDEX, VAR) \\ __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); 復制代碼

    Swift 中的情況

    感謝 @Cyrus_dev 的提醒,在 Swift 中也有 Strong-Weak Dance 的概念。最簡單的方法就是直接沿用 OC 的思路:

    self.completionHandler = { [weak self] in if let strongSelf = self { /// .... } }; 復制代碼

    這種寫法的缺點在于,我們不能寫 if let self = self,因此需要重新定義一個變量 strongSelf,命名方式顯得不夠優雅。

    除此以外還可以使用 Swift 標準庫提供的函數 withExtendedLifetime:

    self.completionHandler = { [weak self] in withExtendedLifetime(self) { /// ... } }; 復制代碼

    這種寫法的缺點在于,self 依然是可選類型的,還需要把它解封后才能使用。

    最后,還有一種解決方案是自定義 withExtendedLifetime函數:

    extension Optional { func withExtendedLifetime(body: T -> Void) { if let strongSelf = self { body(strongSelf) } } } 復制代碼

    至于這種寫法是否更加優雅,就見仁見智了:

    self.completionHandler = { [weak self] in self.withExtendedLifetime { /// 這里用 $0 表示 self } }; 復制代碼

    參考資料

  • What happens when a block is set to nil during its execution?
  • The Weak/Strong Dance in Swift
  • 總結

    以上是生活随笔為你收集整理的对 Strong-Weak Dance的思考的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。