最近開始接手一些iOS工程且都是OC代碼捣辆,雖然大學(xué)時(shí)學(xué)過C語言蔬螟,但早已忘得一干二凈。工程里面的 @weakify 和 @strongify 引起了我的好奇:他們到底是干啥的汽畴?
打破循環(huán)引用的標(biāo)準(zhǔn)方法
假設(shè)我們有一個(gè) model 屬性的 controller耸序,當(dāng)模型中的數(shù)據(jù)發(fā)生變化時(shí)更新 label。為此鲁猩,我們建立了一個(gè)模型:
- (void)setUpModel
{
Model *model = [Model new];
// 該block會(huì)在model的數(shù)據(jù)變化調(diào)用
model.dataChanged = ^(NSString *title) {
self.label.text = title;
};
self.model = model;
}
幾行簡單的代碼便創(chuàng)建了一個(gè)引用循環(huán)坎怪。我們的 controller 引用了 model,而 model 又引用了一個(gè) controller 的 block廓握。好在我們可以通過引入帶有__weak和__strong存儲(chǔ)類型修飾符的局部變量輕松打破這個(gè)引用循環(huán):
Model *model = [Model new];
__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) strongSelf = weakSelf;
strongSelf.label.text = title;
};
self.model = model;
Objective-C 工程里到處都可以看到這種 weak/strong 群魔亂舞的代碼搅窿。它是一種正確的處理方式,但卻很容易出錯(cuò)隙券。當(dāng)引入你新功能讓你的 block 變得越來越大時(shí)男应,最終會(huì)有人在其中使用 self。我們不會(huì)注意到它何時(shí)發(fā)生是尔,編譯器只在最簡單的情況下提供幫助殉了。這時(shí)候 weakify 和 strongify 宏便派上了用場。
@weakify 和 @strongify
@weakify 和 @strongify 宏的原始實(shí)現(xiàn)比較復(fù)雜拟枚,因?yàn)樗鼈兘邮芏鄠€(gè)參數(shù)薪铜。為了使分析更簡單,我將自己的版本說明恩溅,每個(gè)版本只接受一個(gè)參數(shù):
#define weakify(var) __weak typeof(var) AHKWeak_##var = var;
#define strongify(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored "-Wshadow"") \
__strong typeof(var) var = AHKWeak_##var; \
_Pragma("clang diagnostic pop")
有了這些宏隔箍,我們的示例變成了一下形式:
Model *model = [Model new];
weakify(self);
model.dataChanged = ^(NSString *title) {
strongify(self);
self.label.text = title;
};
self.model = model;
最后將編譯成:
Model *model = [Model new];
__weak typeof(self) AHKWeak_self = self;
model.dataChanged = ^(NSString *title) {
__strong typeof(self) self = AHKWeak_self;
self.label.text = title;
};
self.model = model;
在 block 中self被一個(gè)同名的局部變量所掩蓋。self可以在塊內(nèi)安全地使用脚乡,因?yàn)樗昧嗽摼植孔兞垦烟玻瑫r(shí)該變量被強(qiáng)持有,但又僅在塊結(jié)束執(zhí)行之前有效奶稠。
如果我忘記使用了strongify會(huì)怎么樣俯艰?其實(shí):weakify創(chuàng)建一個(gè)新的局部變量,如果沒有使用它锌订,我們會(huì)收到warning:
Unused variable 'AHKWeak_self'
注意:如果在多個(gè)塊中使用了 strongify(self)竹握,則該 warning 也無濟(jì)于事。我們可以通過為每個(gè) block 引入一個(gè)新scope 來解決辆飘,例如:
{
weakify(self);
model.dataChanged = ^(NSString *title) {
strongify(self);
self.label.text = title;
};
}
{
weakify(self);
model.syncFinished = ^(BOOL success) {
strongify(self);
[self update];
};
}
看起來并不優(yōu)美啦辐,但在某些情況下還是很有用。如果您忘記使用weakify蜈项,但有strongify芹关,編譯器會(huì)顯示錯(cuò)誤:
使用未聲明的標(biāo)識(shí)符“AHKWeak_self”
最后
在某些情況下創(chuàng)建新 block 時(shí),我們?nèi)孕枰紤]保留 self(或其他一些對(duì)象)引用的可能性紧卒,這是任何宏也無法幫忙做到的侥衬,需要實(shí)際分析。但當(dāng)使用了weakify 和strongify 宏,以后對(duì) block 的任何更改都會(huì)比標(biāo)準(zhǔn)的weak/strong 方式更安全轴总。