ios 在 dispatch_async 函数中使用弱 self

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21987067/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:33:50  来源:igfitidea点击:

Using weak self in dispatch_async function

iosobjective-c-blocksweak-referencesretain-cycle

提问by Marko Zadravec

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

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

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_asyncnot need a __weak self.

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

See last comment here

在这里看到最后的评论

回答by CouchDeveloper

Assuming, selfis an object pointer to a UIViewController.

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

Things to consider:

需要考虑的事项:

  • A UIViewControlleris 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!

  • 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 strongpointers will be retainedwhen the block will be copied (for example, when dispatched asynchronously), and again releasedwhen the block will be destroyed (after it finished).

  • Captured retainable weakpointers will NOT be retained and not released.

  • AUIViewController是一个“UIKit”对象。UIKit 对象不应在非主线程上发送方法,即 - 这些方法必须仅在主线程上执行!

  • 一个已经被排入队列的块——无论是同步还是异步——最终都会被执行——无论如何!好吧,除非程序在这发生之前终止。

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

  • 捕获的可保留指针将不会被保留和释放。

In your scenario, where you capture selfin 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 selfwill be capturedin the block which is dispatched asynchronously, selfwill be implicitly retained, and releasedagain when the block has been finished.

因为捕获其中被分派块异步自我会被隐保留,并发布当块已经完成一次。

That means, the life-time of selfwill be extendedup until after the block finishes. Notice that your secondblock is dispatched on the main thread, and it's guaranteed that selfis still alive when that block gets executed.

这意味着,self的生命周期将延长到块完成之后。请注意,您的第二个块是在主线程上分派的,并且可以保证在执行该块时self仍然存在。

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

上面的这种“延长寿命”可能是您的程序所需的功能。

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

如果您明确不想延长UIViewController对象的生命周期,而是希望块 - 当它最终执行时 -检查UIViewController对象是否仍然存在,您可以使用 self.__weak 指针。请注意,该块最终会被执行,无论该块是否UIViewController仍然存在或在此期间已被释放。

You might want the block doing "nothing" if the UIViewControllerhas been deallocated beforethe 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: UIKitobjects shall not be sent methods on non-main threads!

请记住:UIKit对象不应在非主线程上发送方法!

One other subtle error may occur due to the fact that UIKitobjects shall execute methods only on the main thread.

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

This can be violated, if a block captures a UIKitobject which is dispatched asynchronously, and executes on a non-mainthread. It then may happen that the block holds the laststrong reference to that UIKitobject. Now, when the block gets eventually executed, the block will be destroyed and the UIKitobject will be released. Since this is the last strongreference to the UIKitobject, 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 deallocmethod is still a method sent to a UIKitobject.

如果块捕获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 strongreference 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 completionwhose value type is a Block. That is, we get an ivar with name _completionwhose type is a Block.

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

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 usersonce 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, selfholds a strong reference to the ivar _completion, which is a block. And the block itself captures self, which causes to retain selfwhen 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 __weakqualified pointer of 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 __blockqualified pointer of selfand eventually setting it nilin the block when it finishes:

    UsersViewController* __block blockSelf = self;
    self.completion = ^(NSArray* users) {
        blockSelf.users = users;
        blockSelf = nil;
    }   
    [usersViewController fetchUsers];
    
  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. 使用self__block限定指针并最终在完成时将其设置在块中:nil

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

See also: Transitioning to ARC Release Notes

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

回答by Warif Akhand Rishi

Swift update:

迅捷更新:

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

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

Swift 4.2:

斯威夫特 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:

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

Swift 2:

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

Popular open source project Alamofireuses this approach.

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

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

使用[weak self]保护 letstrongSelf = self else { return } 习惯用法来延长对象生命周期。

For more info check out swift-style-guide

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