在dispatch_async函数中使用弱自我 [英] Using weak self in dispatch_async function

查看:145
本文介绍了在dispatch_async函数中使用弱自我的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 dispatch_async 里面读了很多关于使用 __弱自我的帖子,现在我有点困惑。

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

如果我有:

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

我需要使用 __弱自我。因为我读过在某些情况下 dispatch_async 不需要 __弱自我

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

在这里查看最后评论

推荐答案

假设, self 是一个对象指针到 UIViewController

Assuming, self is an object pointer to a UIViewController.

需要考虑的事项:


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

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

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

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.

在您的方案中,您在主队列中调度的块中捕获 self ,您无需担心会发生不良事件。

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 捕获异步调度的块中 self 将隐式保留,并在块完成后再次释放

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 在执行该块时仍处于活动状态。

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.

上面的延长寿命可能是您计划的理想功能。

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

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

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 之前被解除分配,你可能希望该块做无 >该块将被执行:

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

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

参见:转换到ARC发行说明

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

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

如果一个块捕获 UIKit,这可能会被违反异步调度的对象,并在非主线程上执行。然后可能会发生该块持有最后对该 UIKit 对象的强引用。现在,当块最终被执行时,块将被销毁,并且将释放 UIKit 对象。由于这是对 UIKit 对象的最后一个引用,因此将对其进行解除分配。但是,这发生在已执行块的线程上 - 这不是主线程!现在,坏事可以(并且通常会)发生,因为 dealloc 方法仍然是发送到 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对象的强指针的块来避免此错误,并向其发送虚拟方法:

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

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

在您的方案中, last strong 引用可能只在在主线程上执行的块。所以,你可以避免这种微妙的错误。 ;)

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. ;)

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

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 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

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

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.

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

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:

UsersViewController.m中的某处

Somewhere in "UsersViewController.m"

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

[self fetchUsers];  // start asynchronous task

此处, self 拥有对ivar _completion ,这是一个块。并且块本身捕获 self ,这导致在调度块被复制时保留 self 。这是一个经典的参考周期。

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.

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

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


  1. 使用 __弱 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];


  • 使用 __ block 限定指针 self 并最终在块中完成设置 nil

  • Using a __block qualified pointer of self and eventually setting it nil in the block when it finishes:

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


  • 参见:过渡到ARC发行说明

    这篇关于在dispatch_async函数中使用弱自我的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆