在dispatch_async函数中使用弱自我

时间:2021-01-09 14:21:40

I read a lot of posts about using __weak self inside dispatch_async, and now I am a litle bit confused.

我阅读了很多关于在dispatch_async中使用__weak self的帖子,现在我对此感到困惑。

if I have :

如果我有 :

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];
    });
});

do I need to use __weak self. Because I read that in some cases dispatch_async not need a __weak self.

我需要使用__weak self。因为我读过在某些情况下dispatch_async不需要__weak self。

See last comment here

请参阅此处的最新评论

2 个解决方案

#1


125  

Assuming, self is an object pointer to a UIViewController.

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

Things to consider:

需要考虑的事项:

  • A UIViewController is a "UIKit" object. UIKit objects shall not be sent methods on non-main threads, that is - those methods must execute on the main thread only!

    UIViewController是一个“UIKit”对象。 UIKit对象不应该在非主线程上发送方法,即 - 那些方法只能在主线程上执行!

  • A block that has been enqueued in a queue - whether this was synchronously or asynchronously - will eventually be executed -- no matter what! Well, unless the program terminates before this can happen.

    一个已排入队列的块 - 无论是同步还是异步 - 最终将被执行 - 无论如何!好吧,除非程序在此之前终止。

  • Captured retainable strong pointers will be retained when the block will be copied (for example, when dispatched asynchronously), and again released when the block will be destroyed (after it finished).

    当块被复制时(例如,异步调度时),将保留捕获的可保留强指针,并在块被销毁时(在完成后)再次释放。

  • Captured retainable weak pointers will NOT be retained and not released.

    捕获的可保留弱指针不会被保留而不会被释放。

In your scenario, where you capture self in the block which is dispatched on the main queue, you don't need to worry that bad things happen.

在您的方案中,您在主队列中调度的块中捕获self,您不必担心会发生不好的事情。

So, why? And what happens actually?

Since self will be captured in the block which is dispatched asynchronously, self will be implicitly retained, and released again when the block has been finished.

由于self将在异步调度的块中捕获,因此将隐式保留self,并在块完成时再次释放self。

That means, the life-time of self will be extended up until after the block finishes. Notice that your second block is dispatched on the main thread, and it's guaranteed that self is still alive when that block gets executed.

这意味着,自我的生命周期将延长至阻止完成之后。请注意,您的第二个块是在主线程上调度的,并且当该块执行时,它保证self仍处于活动状态。

This "extended life" above, might be a desired feature of your program.

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

If you explicitly don't want to extend the life-time of the UIViewController object, and instead want the block - when it finally executes - check whether this UIViewController object does still exist at all, you can use a __weak pointer of self. Note that the block gets eventually executed, no matter whether the UIViewController is still alive or has been deallocated in the mean time.

如果你明确地不想延长UIViewController对象的生命周期,而是想要块 - 当它最终执行时 - 检查这个UIViewController对象是否仍然存在,你可以使用self的__weak指针。请注意,无论UIViewController是否仍处于活动状态或同时已取消分配,该块最终都会被执行。

You might want the block doing "nothing" if the UIViewController has been deallocated before the block will get executed:

如果在块执行之前已取消分配UIViewController,您可能希望块执行“无任何操作”:

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

See also: Transitioning to ARC Release Notes

另请参阅:转换到ARC发行说明

Remember: UIKit objects shall not be sent methods on non-main threads!

One other subtle error may occur do to the fact that UIKit objects shall execute methods only on the main thread.

UIKit对象只能在主线程上执行方法这一事实可能会发生另一个微妙的错误。

This can be violated, if a block captures a UIKit object which is dispatched asynchronously, and executes on a non-main thread. It then may happen that the block holds the last strong reference to that UIKit object. Now, when the block gets eventually executed, the block will be destroyed and the UIKit object will be released. Since this is the last strong reference to the UIKit object, it will be deallocated. However, this happens on the thread where the block has been executed - and this is not the main thread! Now, bad things can (and will usually) happen, since the dealloc method is still a method sent to a UIKit object.

如果一个块捕获异步调度的UIKit对象,并在非主线程上执行,则可能违反此规则。然后可能发生该块保存对该UIKit对象的最后一个强引用。现在,当块最终被执行时,块将被销毁并且UIKit对象将被释放。由于这是对UIKit对象的最后一个强引用,因此将对其进行解除分配。但是,这发生在已执行块的线程上 - 这不是主线程!现在,坏事可以(并且通常会)发生,因为dealloc方法仍然是发送到UIKit对象的方法。

You can avoid this error, by dispatching a block capturing a strong pointer to that UIKit object, and send it a dummy method:

您可以通过调度捕获指向该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
    });
});

In your scenario though, the last strong reference could be only in the block which executes on the main thread. So, you are safe from this subtle error. ;)

但是,在您的方案中,最后一个强引用可能只在主线程上执行的块中。所以,你可以避免这种微妙的错误。 ;)

Edit:

In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object.

在您的设置中,您永远不会有保留周期。如果可保持对象A强引用另一个可保留对象B,并且对象B强引用A,则发生保留周期。请注意,“块”也是可保留对象。

A contrived example with a cyclic reference:

一个带有循环引用的人为例子:

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

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

Here, we have a property completion whose value type is a Block. That is, we get an ivar with name _completion whose type is a Block.

这里,我们有一个属性完成,其值类型是块。也就是说,我们得到一个名为_completion的ivar,其类型为Block。

A client may set a completion handler which should be called when a certain operation has finished. Suppose, the operation fetches a list of Users from a remote server. The plan is to set the property users once the operation finished:

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

The careless approach would accidentally introduce a cyclic reference:

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

Somewhere in "UsersViewController.m"

在“UsersViewController.m”中的某处

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

[self fetchUsers];  // start asynchronous task

Here, self holds a strong reference to the ivar _completion, which is a block. And the block itself captures self, which causes to retain self when the block gets copied when it is dispatched. This is a classic reference cycle.

在这里,self强烈提到了ivar _completion,这是一个块。并且块本身捕获self,这导致在块被调度时复制块时保留self。这是一个经典的参考周期。

In order to avoid that cyclic reference, we have a few alternatives:

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

  1. Using a __weak qualified pointer of self

    使用__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. Using a __block qualified pointer of self and eventually setting it nil in the block when it finishes:

    使用__block限定的self指针,并在完成时最终在块中将其设置为nil:

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

See also: Transitioning to ARC Release Notes

另请参阅:转换到ARC发行说明

#2


21  

Swift update:

An example of this so-called strong-weak dance in swift:

在swift中这种所谓的强弱舞蹈的一个例子:

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()
        })
    }
}

Popular open source project Alamofire uses this approach.

流行的开源项目Alamofire使用这种方法。

Extend object lifetime using the [weak self] and guard let strongSelf = self else { return } idiom.

使用[弱自我]延长对象生命周期并使用guardSelf = self else {return}成语。

For more info check out swift-style-guide

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

Swift 3 update:

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()
        }
    }
}

#1


125  

Assuming, self is an object pointer to a UIViewController.

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

Things to consider:

需要考虑的事项:

  • A UIViewController is a "UIKit" object. UIKit objects shall not be sent methods on non-main threads, that is - those methods must execute on the main thread only!

    UIViewController是一个“UIKit”对象。 UIKit对象不应该在非主线程上发送方法,即 - 那些方法只能在主线程上执行!

  • A block that has been enqueued in a queue - whether this was synchronously or asynchronously - will eventually be executed -- no matter what! Well, unless the program terminates before this can happen.

    一个已排入队列的块 - 无论是同步还是异步 - 最终将被执行 - 无论如何!好吧,除非程序在此之前终止。

  • Captured retainable strong pointers will be retained when the block will be copied (for example, when dispatched asynchronously), and again released when the block will be destroyed (after it finished).

    当块被复制时(例如,异步调度时),将保留捕获的可保留强指针,并在块被销毁时(在完成后)再次释放。

  • Captured retainable weak pointers will NOT be retained and not released.

    捕获的可保留弱指针不会被保留而不会被释放。

In your scenario, where you capture self in the block which is dispatched on the main queue, you don't need to worry that bad things happen.

在您的方案中,您在主队列中调度的块中捕获self,您不必担心会发生不好的事情。

So, why? And what happens actually?

Since self will be captured in the block which is dispatched asynchronously, self will be implicitly retained, and released again when the block has been finished.

由于self将在异步调度的块中捕获,因此将隐式保留self,并在块完成时再次释放self。

That means, the life-time of self will be extended up until after the block finishes. Notice that your second block is dispatched on the main thread, and it's guaranteed that self is still alive when that block gets executed.

这意味着,自我的生命周期将延长至阻止完成之后。请注意,您的第二个块是在主线程上调度的,并且当该块执行时,它保证self仍处于活动状态。

This "extended life" above, might be a desired feature of your program.

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

If you explicitly don't want to extend the life-time of the UIViewController object, and instead want the block - when it finally executes - check whether this UIViewController object does still exist at all, you can use a __weak pointer of self. Note that the block gets eventually executed, no matter whether the UIViewController is still alive or has been deallocated in the mean time.

如果你明确地不想延长UIViewController对象的生命周期,而是想要块 - 当它最终执行时 - 检查这个UIViewController对象是否仍然存在,你可以使用self的__weak指针。请注意,无论UIViewController是否仍处于活动状态或同时已取消分配,该块最终都会被执行。

You might want the block doing "nothing" if the UIViewController has been deallocated before the block will get executed:

如果在块执行之前已取消分配UIViewController,您可能希望块执行“无任何操作”:

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

See also: Transitioning to ARC Release Notes

另请参阅:转换到ARC发行说明

Remember: UIKit objects shall not be sent methods on non-main threads!

One other subtle error may occur do to the fact that UIKit objects shall execute methods only on the main thread.

UIKit对象只能在主线程上执行方法这一事实可能会发生另一个微妙的错误。

This can be violated, if a block captures a UIKit object which is dispatched asynchronously, and executes on a non-main thread. It then may happen that the block holds the last strong reference to that UIKit object. Now, when the block gets eventually executed, the block will be destroyed and the UIKit object will be released. Since this is the last strong reference to the UIKit object, it will be deallocated. However, this happens on the thread where the block has been executed - and this is not the main thread! Now, bad things can (and will usually) happen, since the dealloc method is still a method sent to a UIKit object.

如果一个块捕获异步调度的UIKit对象,并在非主线程上执行,则可能违反此规则。然后可能发生该块保存对该UIKit对象的最后一个强引用。现在,当块最终被执行时,块将被销毁并且UIKit对象将被释放。由于这是对UIKit对象的最后一个强引用,因此将对其进行解除分配。但是,这发生在已执行块的线程上 - 这不是主线程!现在,坏事可以(并且通常会)发生,因为dealloc方法仍然是发送到UIKit对象的方法。

You can avoid this error, by dispatching a block capturing a strong pointer to that UIKit object, and send it a dummy method:

您可以通过调度捕获指向该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
    });
});

In your scenario though, the last strong reference could be only in the block which executes on the main thread. So, you are safe from this subtle error. ;)

但是,在您的方案中,最后一个强引用可能只在主线程上执行的块中。所以,你可以避免这种微妙的错误。 ;)

Edit:

In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object.

在您的设置中,您永远不会有保留周期。如果可保持对象A强引用另一个可保留对象B,并且对象B强引用A,则发生保留周期。请注意,“块”也是可保留对象。

A contrived example with a cyclic reference:

一个带有循环引用的人为例子:

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

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

Here, we have a property completion whose value type is a Block. That is, we get an ivar with name _completion whose type is a Block.

这里,我们有一个属性完成,其值类型是块。也就是说,我们得到一个名为_completion的ivar,其类型为Block。

A client may set a completion handler which should be called when a certain operation has finished. Suppose, the operation fetches a list of Users from a remote server. The plan is to set the property users once the operation finished:

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

The careless approach would accidentally introduce a cyclic reference:

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

Somewhere in "UsersViewController.m"

在“UsersViewController.m”中的某处

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

[self fetchUsers];  // start asynchronous task

Here, self holds a strong reference to the ivar _completion, which is a block. And the block itself captures self, which causes to retain self when the block gets copied when it is dispatched. This is a classic reference cycle.

在这里,self强烈提到了ivar _completion,这是一个块。并且块本身捕获self,这导致在块被调度时复制块时保留self。这是一个经典的参考周期。

In order to avoid that cyclic reference, we have a few alternatives:

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

  1. Using a __weak qualified pointer of self

    使用__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. Using a __block qualified pointer of self and eventually setting it nil in the block when it finishes:

    使用__block限定的self指针,并在完成时最终在块中将其设置为nil:

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

See also: Transitioning to ARC Release Notes

另请参阅:转换到ARC发行说明

#2


21  

Swift update:

An example of this so-called strong-weak dance in swift:

在swift中这种所谓的强弱舞蹈的一个例子:

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()
        })
    }
}

Popular open source project Alamofire uses this approach.

流行的开源项目Alamofire使用这种方法。

Extend object lifetime using the [weak self] and guard let strongSelf = self else { return } idiom.

使用[弱自我]延长对象生命周期并使用guardSelf = self else {return}成语。

For more info check out swift-style-guide

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

Swift 3 update:

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()
        }
    }
}