60

我阅读了很多关于使用__weak selfinside的帖子dispatch_async,现在我有点困惑。

如果我有 :

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.

在此处查看最后一条评论

4

2 回答 2

147

假设,self是一个指向 a 的对象指针UIViewController

需要考虑的事项:

  • AUIViewController是一个“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为 Block 的 ivar。

客户端可以设置一个完成处理程序,当某个操作完成时应该调用它。假设,该操作从远程服务器获取用户列表。计划是在操作完成后设置属性用户:

粗心的方法会不小心引入循环引用:

“UsersViewController.m”中的某处

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

[self fetchUsers];  // start asynchronous task

在这里,self持有对 ivar 的强引用_completion,它是一个块。并且块本身会捕获self,这会导致在调度块被复制时保留self 。这是一个经典的参考循环。

为了避免这种循环引用,我们有一些选择:

  1. 使用self__weak的限定指针

    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. 使用self__block的限定指针并最终在完成时将其设置在块中:nil

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

另请参阅:过渡到 ARC 发行说明

于 2014-02-24T12:53:41.327 回答
31

快速更新:

这种所谓的强弱快速舞蹈的例子:

斯威夫特 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()
        }
    }
}

斯威夫特 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使用这种方法。

使用[weak self]延长对象生命周期并保护 let strongSelf = self else { return } 成语。

有关更多信息,请查看swift-style-guide

于 2016-05-04T11:10:54.150 回答