問題分析
我看過很多文章關(guān)于在dispatch_async的block里面使用_weak self, 但是讓我疑惑的是,以下代碼是否需要必須使用_weak self, 因?yàn)槲乙部吹搅撕芏嘤^點(diǎn)說,在有些情況下不需要使用__weak self.
self.myQueue = dispatch_queue_create("com.biview.core_data", NULL);
dispatch_async(self.myQueue, ^(void){
if(!self.var1){
self.var1 =
}
dispatch_async(dispatch_get_main_queue(), ^(void){
if([self.var2 superview]) {
[self.var2 removeFromSuperview];
}
[self.Label setText:text];
});
});
解析
針對(duì)上面的問題,我們假設(shè):self是指向UIViewController的對(duì)象指針。
考慮以下幾點(diǎn):
- UIViewController是"UIkit"對(duì)象,UIKit對(duì)象不應(yīng)該在非主線程發(fā)送消息,也就是說,這些方法只能在主線程中執(zhí)行。
- 當(dāng)一個(gè)block被添加進(jìn)一個(gè)同步或者異步隊(duì)列,這個(gè)block最終都會(huì)被執(zhí)行,除非在執(zhí)行到它之前應(yīng)用程序被殺死。
- 當(dāng)block被拷貝的時(shí)候,strong類型的指針會(huì)被retained, 當(dāng)block執(zhí)行完畢之后被銷毀的時(shí)候才會(huì)執(zhí)行released操作。
- weak類型的指針不會(huì)被retained和released。
在上面的例子中,self是在主線程的隊(duì)列中,不必?fù)?dān)心有任何bug產(chǎn)生。
究竟發(fā)生了什么?
當(dāng)在dispatch的異步隊(duì)列的block中捕獲到self時(shí),self會(huì)被執(zhí)行retained操作,當(dāng)block執(zhí)行完畢后self執(zhí)行released操作。
這意味著:當(dāng)block執(zhí)行完畢后,self的生命周期才會(huì)結(jié)束。上例中的第二個(gè)block是在主線程的隊(duì)列中,它保證了self一直存活著當(dāng)這個(gè)block被執(zhí)行的時(shí)候。
在程序中存在潛在危險(xiǎn)的操作是:延長(zhǎng) self 的生命周期。
如果你明確的不希望延長(zhǎng)UIViewController對(duì)象的生命周期,而是當(dāng)block被執(zhí)行的時(shí)候去檢查UIViewController對(duì)象到底是否存在,你可以使用 _weak self. 需要注意的是block最后都會(huì)被執(zhí)行,不管UIViewController是否存活還是已經(jīng)被釋放了。
如果你希望如果UIViewController已經(jīng)被釋放了,那么block不做任何事情,可以寫成 _weak self.
MyController * _weak weakSelf = self;
dispatch_async(queue, ^{
MyController *strongSelf = weakSelf;
if(strongSelf){
...
}else {
// self has been deallocted in the meantime.
}
});
不能在非主線程中向UIKit對(duì)象發(fā)送消息。
另一個(gè)細(xì)微的錯(cuò)誤可能發(fā)生在UIKit對(duì)象執(zhí)行方法在非主線程。
如果block在異步線程中捕獲了一個(gè)UIKit對(duì)象,可能發(fā)生的是:block 是最后一個(gè)持有改UIKit的強(qiáng)引用。當(dāng)block執(zhí)行完的時(shí)候,UIKit對(duì)象將被release,因?yàn)槭荱IKit對(duì)象的最后一個(gè)強(qiáng)引用,所有該UIKit對(duì)象將被釋放,但是,釋放操作發(fā)生在block所執(zhí)行的線程-它不是主線程,所有,風(fēng)險(xiǎn)即將發(fā)生,UIKit對(duì)象的dealloc方法將被調(diào)用(UI 對(duì)象應(yīng)該在主線程中被回收,因?yàn)樵谒鼈兊?dealloc 方法被調(diào)用回收的時(shí)候,可能會(huì)去改變 view 的結(jié)構(gòu)關(guān)系,而如我們所知,這種操作應(yīng)該放在主線程來進(jìn)行,見參考二)。
避免這個(gè)錯(cuò)誤:
UIViewController *strongUIKitPointer = ...
dispatch_async(non_main_queue), ^{
...//do someting
dispatch(dispatch_get_main_queue(),^{
[strongUIkitPointer self]; //self is a method, too -doing nothing.
});
});
舉例
雙向強(qiáng)引用發(fā)生在:一個(gè)強(qiáng)類型對(duì)象A持有一個(gè)強(qiáng)類型對(duì)象B,并且對(duì)象B強(qiáng)引用對(duì)象A?!癇lock”是一個(gè)強(qiáng)引用對(duì)象。
人為的雙向強(qiáng)引用舉例:
typedef void(^my_completion_block_t)(NSArray* result);
@interface UserViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray *users;
on "UserViewController.m"
self.completion = ^(NSArray *users){
self.users = users;
}
[self fetchUsers];
這是一個(gè)典型了強(qiáng)引用循環(huán)。UserViewController 有一個(gè)Block類型的屬性,所有UserViewController對(duì)象強(qiáng)引用著block。而block捕獲到self的時(shí)候執(zhí)行強(qiáng)引用操作,所有形成了強(qiáng)引用循環(huán)。
解決方式:
- 使用_weak 指針指向self.
UserViewController * _weak weakSelf = self;
self.completion = ^(NSArray *user){
UsersViewController *strongSelf = weakSelf;
if(strongSelf){
strongSelf.users = users;
}else{
// the view controller does not exist anymore.
}
};
- 使用__block 指針執(zhí)行self, 執(zhí)行完畢后將__block 指針指向nil.
UsersViewController *__block blockSelf = self;
self.completion=^(NSArayy *users){
self.completion = ^(NSArray *users){
blockSelf.users = users;
blockSelf = nil;
}
}
--完--
參考
交流群
移動(dòng)開發(fā)交流群:264706196