在使用 Block
時,除了使用 __weak
修飾符避免循環引用外,還有一點常常容易忘記。蘋果把它稱爲:「Strong-Weak Dance」。git
這是一種 強引用 --> 弱引用 --> 強引用 的變換過程。在弄明白爲何要如此大費周章以前,咱們首先來看看通常的寫法會有什麼問題。github
__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
[wself.property removeObserver: wself forKeyPath:@"pathName"];
};
複製代碼
這種寫法能夠避免循環引用,可是咱們要考慮這樣的問題:swift
假設 block
被放在子線程中執行,並且執行過程當中 self
在主線程被釋放了。因爲 wself
是一個弱引用,所以會自動變爲 nil
。而在 KVO 中,這會致使崩潰。多線程
解決以上問題的方法很簡單,新增一行代碼便可:閉包
__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
__strong __typeof(wself) sself = wself; // 強引用一次
[sself.property removeObserver: sself forKeyPath:@"pathName"];
};
複製代碼
這樣一來,self
所指向對象的引用計數變成 2,即便主線程中的 self
由於超出做用於而釋放,對象的引用計數依然爲 1,避免了對象的銷燬。app
在和小夥伴的討論過程當中,他提出了幾個問題。雖然都不難,可是有利於把各類知識融會貫通起來。函數
__strong __typeof(wself) sself = wself;
複製代碼
A:會的。引用計數描述的是對象而不是指針。這句話的意思是:ui
sself 強引用 wself 指向的那個對象spa
所以對象的引用計數會增長一個。線程
block
內部定義了sself
,會不會所以強引用了 sself
?A:不會。block
只有截獲外部變量時,纔會引用它。若是是內部新建一個,則沒有任何問題。
block
內部沒有強引用,而是經過 if
判斷,是否是也能夠,好比這樣寫:__weak MyViewController *wself = self;
wself.completionHandler = ^(NSInteger result) {
if (wself) { // 只有當 wself 不爲 nil 時,才執行如下代碼
[wself.property removeObserver: wself forKeyPath:@"pathName"];
}
};
複製代碼
A:不能夠!考慮到多線程執行,也許在判斷的時候,self
還沒釋放,可是執行 self
裏面的代碼時,就恰好釋放了。
block
內部強引用也沒用啊。也許 block
執行之前,self
就釋放了。A:有用!若是在 block
執行之前,self
就釋放了,那麼 block
的引用計數降爲 0,因此本身就會被釋放。這樣它根本就不會被執行。另外,若是執行一個爲 nil
的閉包會致使崩潰。
block
的過程當中,block
被釋放了怎麼辦?A:簡單來講,block
還會繼續執行,可是它捕獲的指針會具備不肯定的值,詳細內容請參考這篇文章
這是 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_);
複製代碼
感謝 @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
}
};
複製代碼