iOS 块和对 self 的强/弱引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19018456/
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-31 01:42:42  来源:igfitidea点击:

iOS blocks and strong/weak references to self

iosobjective-cobjective-c-blocks

提问by Mason

I have a question about strong and weak references to self in blocks in 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:

我有一个关于 iOS 块中对 self 的强引用和弱引用的问题。我知道在块内引用 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!

非常感谢任何信息或解释!

回答by Jeff Kelley

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 completionBlockproperty. But since it references selfinside the block, the block will keep selfalive until it goes away—but this won't happen since they're both referencing each other.

当我将它存储在completionBlock属性中时,该块将保持活动状态。但是由于它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. The block will keep selfalive, since it references selffrom within, but since all we're doing is handing the block off to [NSOperationQueue mainQueue], selfisn't keeping the block alive.

你不需要对self. 该块将保持self活动状态,因为它是self从内部引用的,但由于我们所做的只是将块交给[NSOperationQueue mainQueue]self因此不会使该块保持活动状态。

Hope this helps.

希望这可以帮助。

回答by Rob

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? If you do not want it retained in this scenario (which I might guess was your intent, given that you're going through this weakSelfexercise in the first place), then use your final example, where you have the second weak pointer. Otherwise you can use your other example.

两种构造都很好。这仅取决于您的意图。如果对象(a)在外部块开始之后释放,但(b)在主队列上的内部块开始之前释放,您希望发生什么?如果您不希望在这种情况下保留它(我猜这就是您的意图,因为您首先要进行此weakSelf练习),然后使用您的最后一个示例,其中您有第二个弱指针。否则,您可以使用其他示例。

Having said that, a couple of observations:

话虽如此,有几点意见:

  1. It's not a forgone conclusion that you have to use this weakSelfpattern in the first place. Some people mistakenly think that they haveto use this weakSelfpattern to avoid a strong reference cycle (a.k.a. retain cycle). 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 weakSelfpattern makes perfect sense. But that's not always the case.

    But my point is that you shouldn't be pursing this weakSelfpattern (at least in this dispatch_asyncscenario) to avoid a strong reference cycle. No such cycle exists. Where this is an issue is where you have a block variable (e.g. some completionHandlerblock). In that case, the weakSelfpattern is critical. But not here.

  2. But let's consider for a second that scenario in which you don't want selfretained. 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.

    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 (e.g. the PDF or image you're downloading takes up far more system resources (both memory and network bandwidth) than the view controller ever will).

  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. (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 strongSelfconstruct, also. In your case, where you're just calling a single method of self, you don't need to bother with this strongSelfconstruct. That's critical only if you're going to deference ivars or otherwise need to avoid race conditions. But, in this example, given that a message sent to a nilobject does nothing, you technically often don't need to worry about this strongSelfconstruct at all.

  1. 您必须首先使用这种weakSelf模式并不是一个必然的结论。有些人错误地认为他们必须使用这种weakSelf模式来避免强引用循环(又名保留循环)。但是这个代码示例并不构成强引用循环。它只是在分派的代码执行时保留对象,这是一个非常不同的考虑。

    事实上,有时你需要/想要那个。有时你不会。这取决于您要解决的业务问题。绝对地,您经常不希望它保持对 的强引用self,在这种情况下,该weakSelf模式非常有意义。但情况并非总是如此。

    但我的观点是,您不应该追求这种weakSelf模式(至少在这种dispatch_async情况下)以避免强引用循环。不存在这样的循环。这是一个问题的地方是您有一个块变量(例如某个completionHandler块)。在这种情况下,weakSelf模式至关重要。但不是这里。

  2. 但是让我们再考虑一下您不希望self保留的情况。然后有一个问题,您是否希望分派的代码首先继续。如果没有,也许您应该使用具有可取消操作的操作队列而不是 GCD。

    例如,我很惊讶人们在后台网络请求运行时经常为是否要保留视图控制器而苦恼,但不要担心他们是否应该首先取消后台网络请求。通常,后者是一个更重要的设计考虑因素(例如,您下载的 PDF 或图像比视图控制器占用的系统资源(内存和网络带宽)要多得多)。

  3. 但是让我们假设 (a) 您确实希望分派的代码继续执行,但是 (b) 您不想保留self. (这似乎是一种罕见的情况,但这是您所询问的情况,所以让我们继续讨论。)最后一个问题是您是否还需要您的strongSelf构造。在您的情况下,您只是调用 的单个方法self,您不需要打扰这个strongSelf构造。仅当您要遵守 ivars 或需要避免竞争条件时,这才至关重要。但是,在这个例子中,假设发送到nil对象的消息什么都不做,从技术上讲,您通常根本不需要担心这个strongSelf构造。

Don't get me wrong. It's good to get one's arms around the weakSelfpattern, as well as the nested strongSelfpattern that sometimes accompanies it. 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 NSOperationis often a far more critical, but often overlooked, question.

不要误会我的意思。最好能抓住weakSelf图案,以及strongSelf有时伴随它的嵌套图案。我只是建议了解何时真正需要这些模式是很好的。而且我认为 GCD 与可取消的选择NSOperation通常是一个更为关键但经常被忽视的问题。

回答by user3378170

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

如果块成为实例变量 ARC,则将该块从堆栈复制到堆。您可以使用复制消息显式复制块。您的块现在是基于堆的块而不是基于堆栈的块。而且您必须处理一些内存管理问题。块本身将保留对其引用的任何对象的强引用。在块外声明 __weak 指针,然后在块内引用该指针以避免循环保留。