dispatch_async 的 block 中是否該使用_weak self

問題分析

我看過不少文章關於在dispatch_async的block裏面使用_weak self, 可是讓我疑惑的是,如下代碼是否須要必須使用_weak self, 由於我也看到了不少觀點說,在有些狀況下不須要使用__weak self.objective-c

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];
    });
});複製代碼

解析

針對上面的問題,咱們假設:self是指向UIViewController的對象指針。
考慮如下幾點:iview

  • UIViewController是"UIkit"對象,UIKit對象不該該在非主線程發送消息,也就是說,這些方法只能在主線程中執行。
  • 當一個block被添加進一個同步或者異步隊列,這個block最終都會被執行,除非在執行到它以前應用程序被殺死。
  • 當block被拷貝的時候,strong類型的指針會被retained, 當block執行完畢以後被銷燬的時候纔會執行released操做。
  • weak類型的指針不會被retained和released。

在上面的例子中,self是在主線程的隊列中,沒必要擔憂有任何bug產生。異步

究竟發生了什麼?

當在dispatch的異步隊列的block中捕獲到self時,self會被執行retained操做,當block執行完畢後self執行released操做。
這意味着:當block執行完畢後,self的生命週期纔會結束。上例中的第二個block是在主線程的隊列中,它保證了self一直存活着當這個block被執行的時候。
在程序中存在潛在危險的操做是:延長 self 的生命週期。async

若是你明確的不但願延長UIViewController對象的生命週期,而是當block被執行的時候去檢查UIViewController對象究竟是否存在,你可使用 _weak self. 須要注意的是block最後都會被執行,無論UIViewController是否存活仍是已經被釋放了。fetch

若是你但願若是UIViewController已經被釋放了,那麼block不作任何事情,能夠寫成 _weak self.atom

MyController * _weak weakSelf = self;
dispatch_async(queue, ^{
    MyController *strongSelf = weakSelf;
    if(strongSelf){
        ...
    }else {
        // self has been deallocted in the meantime.
    }
});複製代碼

不能在非主線程中向UIKit對象發送消息。
另外一個細微的錯誤可能發生在UIKit對象執行方法在非主線程。spa

若是block在異步線程中捕獲了一個UIKit對象,可能發生的是:block 是最後一個持有改UIKit的強引用。當block執行完的時候,UIKit對象將被release,由於是UIKit對象的最後一個強引用,全部該UIKit對象將被釋放,可是,釋放操做發生在block所執行的線程-它不是主線程,全部,風險即將發生,UIKit對象的dealloc方法將被調用(UI 對象應該在主線程中被回收,由於在它們的 dealloc 方法被調用回收的時候,可能會去改變 view 的結構關係,而如咱們所知,這種操做應該放在主線程來進行,見參考二)。線程

避免這個錯誤:指針

UIViewController *strongUIKitPointer = ...
dispatch_async(non_main_queue), ^{
    ...//do someting
    dispatch(dispatch_get_main_queue(),^{
        [strongUIkitPointer self]; //self is a method, too -doing nothing.
    });
});複製代碼

舉例

雙向強引用發生在:一個強類型對象A持有一個強類型對象B,而且對象B強引用對象A。「Block」是一個強引用對象。code

人爲的雙向強引用舉例:

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];複製代碼

這是一個典型了強引用循環。UserViewController 有一個Block類型的屬性,全部UserViewController對象強引用着block。而block捕獲到self的時候執行強引用操做,全部造成了強引用循環。

解決方式:

  1. 使用_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.
     }
    };複製代碼
  2. 使用block 指針執行self, 執行完畢後將block 指針指向nil.

    UsersViewController *__block blockSelf = self;
    self.completion=^(NSArayy *users){
     self.completion = ^(NSArray *users){
         blockSelf.users = users;
         blockSelf = nil;
     }
    }複製代碼

--完--

參考

  1. stack overflow
  2. objc-cn
相關文章
相關標籤/搜索