对 Strong-Weak Dance的思考
在使用 Block 時(shí),除了使用 __weak 修飾符避免循環(huán)引用外,還有一點(diǎn)經(jīng)常容易忘記。蘋果把它稱為:“Strong-Weak Dance”。
問題來源
這是一種 強(qiáng)引用 --> 弱引用 --> 強(qiáng)引用 的變換過程。在弄明白為什么要如此大費(fèi)周章之前,我們首先來看看一般的寫法會(huì)有什么問題。
__weak MyViewController *wself = self; self.completionHandler = ^(NSInteger result) { [wself.property removeObserver: wself forKeyPath:@"pathName"]; }; 復(fù)制代碼這種寫法可以避免循環(huán)引用,但是我們要考慮這樣的問題:
假設(shè) block 被放在子線程中執(zhí)行,而且執(zhí)行過程中 self 在主線程被釋放了。由于 wself 是一個(gè)弱引用,因此會(huì)自動(dòng)變?yōu)?nil。而在 KVO 中,這會(huì)導(dǎo)致崩潰。
Strong-Weak Dance
解決以上問題的方法很簡(jiǎn)單,新增一行代碼即可:
__weak MyViewController *wself = self; self.completionHandler = ^(NSInteger result) { __strong __typeof(wself) sself = wself; // 強(qiáng)引用一次 [sself.property removeObserver: sself forKeyPath:@"pathName"]; }; 復(fù)制代碼這樣一來,self 所指向?qū)ο蟮囊糜?jì)數(shù)變成 2,即使主線程中的 self 因?yàn)槌鲎饔糜诙尫?#xff0c;對(duì)象的引用計(jì)數(shù)依然為 1,避免了對(duì)象的銷毀。
思考
在和小伙伴的討論過程中,他提出了幾個(gè)問題。雖然都不難,但是有利于把各種知識(shí)融會(huì)貫通起來。
A:會(huì)的。引用計(jì)數(shù)描述的是對(duì)象而不是指針。這句話的意思是:
sself 強(qiáng)引用 wself 指向的那個(gè)對(duì)象
因此對(duì)象的引用計(jì)數(shù)會(huì)增加一個(gè)。
A:不會(huì)。block 只有截獲外部變量時(shí),才會(huì)引用它。如果是內(nèi)部新建一個(gè),則沒有任何問題。
A:不可以!考慮到多線程執(zhí)行,也許在判斷的時(shí)候,self 還沒釋放,但是執(zhí)行 self 里面的代碼時(shí),就剛好釋放了。
A:有用!如果在 block 執(zhí)行以前,self 就釋放了,那么 block 的引用計(jì)數(shù)降為 0,所以自己就會(huì)被釋放。這樣它根本就不會(huì)被執(zhí)行。另外,如果執(zhí)行一個(gè)為 nil 的閉包會(huì)導(dǎo)致崩潰。
A:簡(jiǎn)單來說,block 還會(huì)繼續(xù)執(zhí)行,但是它捕獲的指針會(huì)具有不確定的值,詳細(xì)內(nèi)容請(qǐng)參考這篇文章
@strongify 和 @weakify
這是 ReactiveCocoa 中定義的一個(gè)宏。一般可以這樣使用:
@weakify(self); self.completionHandler = ^(NSInteger result) { @strongify(self); [self.property removeObserver: sself forKeyPath:@"pathName"]; }; 復(fù)制代碼本文并非分析它們的實(shí)現(xiàn)原理,所以就簡(jiǎn)單解釋兩點(diǎn):
這里的“@”沒有任何用處,僅表示強(qiáng)調(diào),這個(gè)宏實(shí)際上包含了一個(gè)空的 AutoreleasePool,這也就是為什么一定要加上“@”。
它的原理還是和之前一樣,生成了一段形如 __weak MyViewController *wself = self; 這種格式的代碼:
Swift 中的情況
感謝 @Cyrus_dev 的提醒,在 Swift 中也有 Strong-Weak Dance 的概念。最簡(jiǎn)單的方法就是直接沿用 OC 的思路:
self.completionHandler = { [weak self] in if let strongSelf = self { /// .... } }; 復(fù)制代碼這種寫法的缺點(diǎn)在于,我們不能寫 if let self = self,因此需要重新定義一個(gè)變量 strongSelf,命名方式顯得不夠優(yōu)雅。
除此以外還可以使用 Swift 標(biāo)準(zhǔn)庫提供的函數(shù) withExtendedLifetime:
self.completionHandler = { [weak self] in withExtendedLifetime(self) { /// ... } }; 復(fù)制代碼這種寫法的缺點(diǎn)在于,self 依然是可選類型的,還需要把它解封后才能使用。
最后,還有一種解決方案是自定義 withExtendedLifetime函數(shù):
extension Optional { func withExtendedLifetime(body: T -> Void) { if let strongSelf = self { body(strongSelf) } } } 復(fù)制代碼至于這種寫法是否更加優(yōu)雅,就見仁見智了:
self.completionHandler = { [weak self] in self.withExtendedLifetime { /// 這里用 $0 表示 self } }; 復(fù)制代碼參考資料
總結(jié)
以上是生活随笔為你收集整理的对 Strong-Weak Dance的思考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springmvc+mybatis+du
- 下一篇: 9.6Gbps WiFi联盟宣布802.