簡體   English   中英

在dispatch_async函數中使用弱自我

[英]Using weak self in dispatch_async function

我讀了很多關於在dispatch_async使用__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];
    });
});

我需要使用__weak self 因為我讀過在某些情況下dispatch_async不需要__weak self

請參閱此處的最新評論

假設, self是一個指向UIViewController的對象指針。

需要考慮的事項:

  • UIViewController是一個“UIKit”對象。 UIKit對象不應該在非主線程上發送方法,即 - 那些方法只能在主線程上執行!

  • 已排隊的塊 - 無論是同步還是異步 - 最終將被執行 - 無論如何! 好吧,除非程序在此之前終止。

  • 當復制塊時(例如,異步調度時)將保留捕獲的可保留 指針,並在塊被銷毀時(在完成后)再次釋放

  • 捕獲的可保留指針不會被保留而不會被釋放。

在您的方案中,您在主隊列中調度的塊中捕獲self ,您不必擔心會發生不好的事情。

所以為什么? 實際上會發生什么?

由於self將在異步調度的塊中捕獲 ,因此將隱式保留 self ,並在塊完成時再次釋放 self

這意味着, 自我的生命期將延長至阻止完成之后。 請注意,您的第二個塊是在主線程上調度的,並且當該塊執行時,它保證self仍處於活動狀態。

上面的“延長壽命”可能是您程序的理想功能。

如果你明確地不想延長UIViewController對象的生命周期,而是想要塊 - 當它最終執行時 - 檢查這個UIViewController對象是否仍然存在,你可以使用self的__weak指針。 請注意,無論UIViewController是否仍處於活動狀態或者同時已取消分配,該塊最終都會被執行。

如果在執行塊之前已取消分配UIViewController您可能希望塊執行“無任何操作”:

MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
    MyController* strongSelf = weakSelf;
    if (strongSelf) {
        ...
    }
    else {
       // self has been deallocated in the meantime.
    }
});

另請參閱: 轉換到ARC發行說明

請記住: UIKit對象不應該在非主線程上發送方法!

由於UIKit對象僅在主線程上執行方法,因此可能會發生另一個細微錯誤。

如果一個塊捕獲異步調度的UIKit對象,並在非主線程上執行,則可能違反此規則。 然后可能發生該塊保存對該UIKit對象的最后一個強引用。 現在,當塊最終被執行時,塊將被銷毀並且UIKit對象將被釋放。 由於這是對UIKit對象的最后一個引用,因此將取消分配。 但是,這發生在已執行塊的線程上 - 這不是主線程! 現在,壞事可以(並且通常會)發生,因為dealloc方法仍然是發送到UIKit對象的方法。

您可以通過調度捕獲指向該UIKit對象的強指針的塊來避免此錯誤,並向其發送一個虛擬方法:

UIViewController* strongUIKitPointer = ... 
dispatch_async(non_main_queue, ^{
    ... // do something 
    dispatch(dispatch_get_main_queue(), ^{
        [strongUIKitPointer self];  // note: self is a method, too - doing nothing
    });
});

但是,在您的方案中, 最后一個強引用可能只在主線程上執行的塊中。 因此,您可以避免這種微妙的錯誤。 ;)

編輯:

在您的設置中,您永遠不會有保留周期。 如果可保留對象A強引用另一個可保留對象B,並且對象B強引用A,則發生保留周期。請注意,“塊”也是可保留對象。

一個帶有循環引用的人為例子:

typedef void(^my_completion_block_t)(NSArray* result);

@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end

在這里,我們有一個屬性完成,其值類型是塊。 也就是說,我們得到一個名為_completion的ivar,其類型為Block。

客戶端可以設置完成處理程序,當某個操作完成時應該調用該處理程序。 假設,該操作從遠程服務器獲取用戶列表。 計划是在操作完成后設置屬性用戶

不小心的方法會不小心引入循環引用:

在“UsersViewController.m”中的某處

self.completion = ^(NSArray* users){
    self.users = users;
}

[self fetchUsers];  // start asynchronous task

在這里, self強烈提到了ivar _completion ,這是一個塊。 並且塊本身捕獲self ,這導致在塊被調度時復制塊時保留self 這是一個經典的參考周期。

為了避免這種循環引用,我們有幾個選擇:

  1. 使用__weak限定的self指針

     UsersViewController* __weak weakSelf = self; self.completion = ^(NSArray* users) { UsersViewController* strongSelf = weakSelf; if (strongSelf) { strongSelf.users = users; } else { // the view controller does not exist anymore } } [usersViewController fetchUsers]; 
  2. 使用__block限定的self指針,並在完成時最終在塊中將其設置為nil

     UsersViewController* __block blockSelf = self; self.completion = ^(NSArray* users) { blockSelf.users = users; blockSelf = nil; } [usersViewController fetchUsers]; 

另請參閱: 轉換到ARC發行說明

Swift更新:

在swift中這種所謂的強弱舞蹈的一個例子:

Swift 4.2:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let self = self else { return }
            self.updateView()
        }
    }
}

Swift 3&4:

func doSomeThingAsynchronously() {
    DispatchQueue.global().async {
        // Do task in default queue
        DispatchQueue.main.async { [weak self] in
            // Do task in main queue
            guard let strongSelf = self else { return }
            strongSelf.updateView()
        }
    }
}

斯威夫特2:

func doSomeThingAsynchronously() {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in
        // Do task in default queue
        dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in
            guard let strongSelf = self else { return }
            // Do task in main queue
            strongSelf.updateView()
        })
    }
}

流行的開源項目Alamofire使用這種方法。

使用[弱自我]延長對象的生命周期和守護讓 strongSelf =自其他{}回報成語。

有關更多信息,請查看swift-style-guide

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM