简体   繁体   English

在从 UIViewController 调用的非保留完成中引用 self 时,weakSelf/strongSelf 舞蹈真的有必要吗?

[英]Is the weakSelf/strongSelf dance really necessary when referencing self inside a non-retained completion called from a UIViewController?

Say I have the following method inside a UIViewController subclass:假设我在UIViewController子类中有以下方法:

- (void)makeAsyncNetworkCall
{
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
                [self.activityIndicatorView stopAnimating];
            }
        });
    }];
}

I know that the reference to self inside the block results in the UIViewController instance being retained by the block.我知道块内对self的引用会导致UIViewController实例被块保留。 As long as performAsyncNetworkCallWithCompletion does not store the block in a property (or ivar) on my NetworkService , am I right in thinking there is no retain cycle?只要performAsyncNetworkCallWithCompletion不将块存储在我的NetworkService上的属性(或 ivar)中,我是否认为没有保留周期?

I realise this structure above will lead to the UIViewController being retained until performAsyncNetworkCallWithCompletion finishes, even if it is released by the system earlier.我意识到上面的这个结构会导致 UIViewController 被保留直到performAsyncNetworkCallWithCompletion完成,即使它被系统提前释放。 But is it likely (or even possible?) the system will deallocate my UIViewController at all ( after the changes to the way iOS 6 manages a UIViewController 's backing CALayer memory )?但是,它可能(甚至可能吗?),系统会收回我UIViewController在所有更改到iOS 6的一个管理方式后UIViewController的后盾CALayer内存)?

If there is a reason I must do the "weakSelf/strongSelf dance", it would look like this:如果有什么原因我必须跳“weakSelf/strongSelf 舞蹈”,它看起来像这样:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
                [strongSelf.activityIndicatorView stopAnimating];
            }
        });
    }];
}

But I find this unconscionably ugly and would like to avoid it if it's not necessary.但我觉得这不合情理地丑陋,如果没有必要,我想避免它。

As I believe you correctly diagnosed, using self will not necessarily cause strong reference cycle in this scenario.正如我相信您正确诊断的那样,在这种情况下使用self不一定会导致强引用循环。 But this will retain the view controller while the network operation completes, and in this case (as in most cases), there's no need to.但这将在网络操作完成时保留视图控制器,在这种情况下(在大多数情况下),没有必要。 Thus, it may not be necessary to do use weakSelf , but probably prudent to do so.因此,可能没有必要使用weakSelf ,但这样做可能是谨慎的。 It minimizes the chance of an accidental strong reference cycle and leads to more efficient use of memory (releasing the memory associated with the view controller as soon as that view controller is dismissed rather than unnecessarily retaining the view controller until after the network operation is complete).它最大限度地减少了意外强引用循环的可能性,并导致更有效地使用内存(在视图控制器被解除后立即释放与视图控制器关联的内存,而不是在网络操作完成之前不必要地保留视图控制器) .

There is no need for the strongSelf construct, though.但是,不需要strongSelf构造。 You can:你可以:

- (void)makeAsyncNetworkCall
{
    __weak typeof(self) weakSelf = self;
    [self.networkService performAsyncNetworkCallWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf.activityIndicatorView stopAnimating];
        });
    }];
}

You only need the weakSelf / strongSelf combination where it's critical to have a strong reference (eg, you're dereferencing ivars) or if you need to worry about race conditions.您只需要weakSelf / strongSelf组合,在这种情况下,强引用至关重要(例如,您正在取消引用ivars)或者您需要担心竞争条件。 That does not appear to be the case here.这里似乎不是这种情况。

I think the issue is that the networkService may keep a strong reference to the block.我认为问题在于 networkService 可能会保留对该块的强引用。 And the view controller may have a strong reference to the networkService.并且视图控制器可能对 networkService 有很强的引用。 So the possible cycle of VC->NetworkService->block->VC could exist.所以可能存在VC->NetworkService->block->VC的循环。 However, in this case, it's usually safe to assume that the block will be released after it has run, in which case the cycle is broken.但是,在这种情况下,通常可以安全地假设块在运行后将被释放,在这种情况下,循环被打破。 So, in this case, it isn't necessary.所以,在这种情况下,没有必要。

Where it is necessary is if the block is not released.如果块没有被释放,则是必要的。 Say, instead of having a block that runs once after a network call, you have a block that is used as a callback.比方说,你有一个用作回调的块,而不是在网络调用后运行一次的块。 ie the networkService object maintains a strong reference to the block and uses it for all callbacks.即 networkService 对象维护对块的强引用,并将其用于所有回调。 In this case, the block will have a strong reference to the VC, and this will create a strong cycle, so a weak reference is preferred.在这种情况下,块将具有对 VC 的强引用,这将创建一个强循环,因此首选弱引用。

不,如果您的 self.networkService 不将其用作块属性,您应该没问题

The answer is not so straightforward here.这里的答案并不是那么简单。 I agree with @Rob's answer, but it needs additional explanation:我同意@Rob 的回答,但需要额外的解释:

  1. __weak is considered as a safe way, since it nils the self when released, meaning there will be no exception if callback happens much later when the calling object is already released, referenced by block, like UIViewController popped from the stack. __weak被认为是一种安全的方式,因为它在释放时将 self 置为 nil,这意味着如果在调用对象已经被释放、被块引用(如UIViewController从堆栈中弹出)之后发生回调,则不会有任何异常。 Adding the possibility of cancelling any kind of operation is her merely a matter of hygiene and perhaps resources as well.增加取消任何类型手术的可能性只是她的卫生问题,也许也是资源问题。 You can, for example also just cancel NSURLConnection , it's not only NSOperation that can be canceled, you can cancel whatever is being executed asynchronously in the method that calls back to block.例如,您也可以取消NSURLConnection ,不仅可以取消NSOperation ,您还可以取消在回调到阻止的方法中异步执行的任何内容。

  2. If self is let to be retained by the block, then the story can get a bit complicated if the caller object like UIViewController is being released by UINavigationController and block still retains it and calls back.如果让 self 由 block 保留,那么如果调用者对象(如UIViewControllerUINavigationController释放并且 block 仍然保留它并回调),则故事可能会变得有点复杂。 In this case callback block will be executed and assumed some data will be changed by results of it.在这种情况下,回调块将被执行,并假设一些数据将被它的结果改变。 That might be even wanted behaviour, but in most of the cases not.这甚至可能是想要的行为,但在大多数情况下不是。 Therefore the cancelling of operation might be more vital in this case, making it very wise in UINavigationControllerDelegate methods by cancelling asynchronous tasks from the mutable collection that reside either on UINavigationController as associated object or as a singleton.因此,在这种情况下取​​消操作可能更为重要,通过从作为关联对象或作为单例驻留在UINavigationController上的可变集合中取消异步任务,在UINavigationControllerDelegate方法中非常明智。

Save bet is with first option, of course, but only in the case you don't want asynchronous operation to continue after you dismiss the caller object.当然,保存赌注是第一个选项,但仅在您不希望异步操作在您关闭调用者对象后继续的情况下。

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

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