简体   繁体   English

iOS阻止和对自我的强/弱引用

[英]iOS blocks and strong/weak references to self

I have a question about strong and weak references to self in blocks in iOS. 我有一个关于iOS中块的自我强弱参考的问题。 I know the proper way to refer to self inside a block is to create a weak reference outside the block, and then a strong reference to that weak reference inside the block, like this: 我知道在块内引用self的正确方法是在块外创建一个弱引用,然后在块内强引用该弱引用,如下所示:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
    typeof(self) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf.someProperty);
});

However, what happens if you have nested blocks? 但是,如果你有嵌套块会发生什么? Is the one set of references enough? 一组参考文献足够吗? Or do you need a new set for each block? 或者你需要为每个街区设置一套新套装? For example, which of the following is correct? 例如,以下哪项是正确的?

This: 这个:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
    typeof(self) strongSelf = weakSelf;
    NSLog(@"%@", strongSelf.someProperty);
    dispatch_async(dispatch_get_main_queue(), ^ {
        strongSelf.view.frame = CGRectZero;
    });
});

Or this: 或这个:

__weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^ {
        typeof(self) strongSelf = weakSelf;
        NSLog(@"%@", strongSelf.someProperty);
        __weak typeof(strongSelf) weakSelf1 = strongSelf;
        dispatch_async(dispatch_get_main_queue(), ^ {
            typeof(strongSelf) strongSelf1 = weakSelf1;
            strongSelf1.view.frame = CGRectZero;
        });
    });

Any information or explanation is much appreciated! 非常感谢任何信息或解释!

You don't need to make two sets of weak references. 您不需要制作两组弱引用。 What you want to avoid with blocks is a retain cycle—two objects keeping each other alive unnecessarily. 你想要用块来避免的是一个保留周期 - 两个对象让对方不必要地活着。

If I have an object with this property: 如果我有一个具有此属性的对象:

@property (strong) void(^completionBlock)(void);

and I have this method: 我有这个方法:

- (void)doSomething
{
    self.completionBlock = ^{
        [self cleanUp];
    };

    [self doLongRunningTask];
}

the block will be kept alive when I store it in the completionBlock property. 当我将它存储在completionBlock属性中时,该块将保持活动状态。 But since it references self inside the block, the block will keep self alive until it goes away—but this won't happen since they're both referencing each other. 但是因为它在块中引用self ,所以块会保持self活着直到它消失 - 但这不会发生,因为它们都是相互引用的。

In this method: 在这个方法中:

- (void)doSomething
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self cleanUp];
    }];

    [self doLongRunningTask];
}

you don't need to make a weak reference to self . 你不需要做一个弱引用self The block will keep self alive, since it references self from within, but since all we're doing is handing the block off to [NSOperationQueue mainQueue] , self isn't keeping the block alive. 该块将保持self活着,因为它从内部引用self ,但由于我们所做的只是将块移交给[NSOperationQueue mainQueue]self不会使块保持活动状态。

Hope this helps. 希望这可以帮助。

Both constructs are fine. 两种结构都很好。 It just depends upon your intent. 这取决于你的意图。 What do you want to happen if the object is (a) released after the outer block commences but (b) before the inner block starts on the main queue? 如果对象是(a)在外部块开始之后释放但是(b)在内部块在主队列上开始之前,你想要发生什么? If you do not want it retained in this scenario (which I might guess was your intent, given that you're going through this weakSelf exercise in the first place), then use your final example, where you have the second weak pointer. 如果您不希望它保留在这种情况下(我可能会猜测是您的意图,假设您首先进行了这个weakSelf ),那么请使用您的最后一个示例,其中您有第二个弱指针。 Otherwise you can use your other example. 否则你可以使用你的另一个例子。

Having said that, a couple of observations: 话虽如此,有几点意见:

  1. It's not a forgone conclusion that you have to use this weakSelf pattern in the first place. 你不得不首先使用这种weakSelf模式,这不是一个weakSelf结论。 Some people mistakenly think that they have to use this weakSelf pattern to avoid a strong reference cycle (aka retain cycle). 有些人错误地认为他们必须使用这种weakSelf模式来避免强烈的参考周期(也就是保留周期)。 But this code sample does not constitute a strong reference cycle. 但是这个代码示例并不构成强大的参考周期。 It simply retains the object while the dispatched code executes, which is a very different consideration. 它只是在调度代码执行时保留对象,这是一个非常不同的考虑因素。

    In fact, sometimes you need/want that. 事实上,有时你需要/想要那个。 Sometimes you don't. 有时候你没有。 It depends upon the business problem you're solving. 这取决于您正在解决的业务问题。 Absolutely, you frequently don't want it to keep a strong reference to self , in which case the weakSelf pattern makes perfect sense. 当然,你经常不希望它对self有强烈的引用,在这种情况下, weakSelf模式非常有意义。 But that's not always the case. 但情况并非总是如此。

    But my point is that you shouldn't be pursing this weakSelf pattern (at least in this dispatch_async scenario) to avoid a strong reference cycle. 但我的观点是,你不应该追求这种weakSelf模式(至少在这个dispatch_async场景中)以避免强大的参考周期。 No such cycle exists. 没有这样的循环。 Where this is an issue is where you have a block variable (eg some completionHandler block). 这是一个问题,你有一个块变量(例如一些completionHandler块)。 In that case, the weakSelf pattern is critical. 在这种情况下, weakSelf模式至关重要。 But not here. 但不是在这里。

  2. But let's consider for a second that scenario in which you don't want self retained. 但是让我们考虑一下你不希望self保留的情况。 Then there's a question of whether you want the dispatched code continuing at all in the first place. 然后是一个问题,你是否希望首先继续发送代码。 If not, maybe you should be using a operation queue with cancelable operations instead of GCD. 如果没有,也许您应该使用具有可取消操作的操作队列而不是GCD。

    For example, I'm surprised how often people agonize over whether they're going to retain the view controller while some background network request is running, but don't worry about whether they should be canceling that background network request in the first place. 例如,我很惊讶人们在一些后台网络请求运行时是否经常会保留视图控制器时会感到痛苦,但是不要担心他们是否应该首先取消该后台网络请求。 Often, the latter is a far more significant design consideration (eg the PDF or image you're downloading takes up far more system resources (both memory and network bandwidth) than the view controller ever will). 通常,后者是一个更重要的设计考虑因素(例如,您下载的PDF或图像占用的视频控制器将占用更多的系统资源(内存和网络带宽))。

  3. But let's assume that (a) you really want the dispatched code to continue to execute, but (b) you don't want to retain self . 但是我们假设(a)你真的希望调度的代码继续执行,但是(b)你不想保留self (This seems like a rare scenario, but it's the one you've asked about, so let's pursue that.) The final question of whether you need your strongSelf construct, also. (这似乎是一个罕见的场景,但它是你所问过的那个,所以让我们继续追求。)最后一个问题是你是否需要你的strongSelf构造。 In your case, where you're just calling a single method of self , you don't need to bother with this strongSelf construct. 在你的情况下,你只需要调用一个self方法,你不需要打扰这个strongSelf结构。 That's critical only if you're going to deference ivars or otherwise need to avoid race conditions. 只有当你要顺从ivars或者需要避免竞争条件时,这才是至关重要的。 But, in this example, given that a message sent to a nil object does nothing, you technically often don't need to worry about this strongSelf construct at all. 但是,在这个例子中,假设发送给nil对象的消息什么都不做,你在技术上通常根本不需要担心这个strongSelf构造。

Don't get me wrong. 别误会我的意思。 It's good to get one's arms around the weakSelf pattern, as well as the nested strongSelf pattern that sometimes accompanies it. 让自己围绕weakSelf模式,以及有时伴随它的嵌套strongSelf模式是很好的。 I'm just suggesting it's good to understand when these patterns are truly needed. 我只是建议理解何时真正需要这些模式。 And I think the choice of GCD versus a cancelable NSOperation is often a far more critical, but often overlooked, question. 而且我认为GCD与可取消的NSOperation的选择往往是一个更为关键但却经常被忽视的问题。

Blocks are created and stored on the stack. 创建块并将其存储在堆栈中。 So the block will be destroyed when the method that created the block returns. 因此,当创建块的方法返回时,块将被销毁。

If a block becomes an instance variable ARC copy the block from the stack to the heap. 如果块成为实例变量ARC,则将块从堆栈复制到堆。 You can explicit copy a block with the copy message. 您可以使用复制消息显式复制块。 Your block is now a heap-based block instead of a stack-based block. 您的块现在是基于堆的块而不是基于堆栈的块。 And you have to deal with some memory management issues. 而且你必须处理一些内存管理问题。 The block itself will keep a strong reference to any objects it references. 块本身将对它引用的任何对象保持强引用。 Declare __weak pointers outside the block and then reference this pointer within the block to avoid retain cycles. 在块外部声明__weak指针,然后在块内引用此指针以避免保留周期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM