关于C#:在iOS上使用带有ARC的块时,确保对象存在并且不会泄漏的正确方法是什么?

What is the correct way to be sure that the object will be there and it won't leak while using blocks with ARC on iOS?

以下哪个代码部分是正确的?

正确定义"我":

  • 它不应该有保留周期,因此不应泄漏。
  • 它必须保证method2和method3的运行。 所以块中的MyObject变量绝不能是nil。(可能出现在__weak定义中......)(我不想检查它是否为nil以防止崩溃。我总是希望它是非零的)

更新:(附加信息)
仪器工具确实显示奇怪的泄漏,直到我用__weak替换__block。 然而,在那之后,我记得__弱引用可能随时消失。 我必须确保它不会消失并且也会泄漏。 我没有计时器。 someMethod在观察特定NSNotification时在主线程上调用。

@implementation MyObject...

1
2
3
4
5
6
7
8
9
10
-(void)someMethod{
    AnotherObject *abc=[[AnotherObject alloc]init];
    __weak MyObject *weakSelf=self;
    abc.onSuccess=^{
         __strong MyObject * strongSelf = weakSelf;
        [strongSelf method2];
        [strongSelf method3];
    }

}

要么

1
2
3
4
5
6
7
8
9
10
11
12
13
@implementation MyObject...

-(void)someMethod{
    AnotherObject *abc=[[AnotherObject alloc]init];
    __block MyObject *blockSelf=self;
    abc.onSuccess=^{
        [blockSelf method2];
        [blockSelf method3];

        blockSelf=nil;
    }

}

更新2:如果我不使用__weak总是泄漏的实际代码:

1
2
3
4
5
    __block RButton *_self=self;
    _aimageView.onSuccess=^(void){
        [_self.headerLabel setText:[_self.book title]];
        _self = nil;
    };

我在"更新2"中查看了您的实际代码,但它缺少很多信息。例如,_aimageView是一个实例变量吗?局部变量?要不然是啥?什么原因导致这个onSuccess被调用?

如果_aimageView是局部变量,那么我没有看到保留周期的证据。

我的第一个想法是我不明白你为什么要保证块的主体内的代码在这个例子中运行。看起来里面的代码只是更新一些UI元素。好吧,如果UI不再显示(如当前对象除了可能通过此块之外没有对它的引用),那么更新UI元素的重点是什么?

了解导致onSuccess被调用的原因非常重要,因为不同类型的回调需要不同的内存管理体系结构。例如,如果块被触发以响应触摸或某种类似的事件,则很可能self指向的对象(可能是某种视图或视图控制器)必须仍处于活动状态。命令事件发生。如果是这种情况,那么__weak将做你想要的。

基本上,通过命名这些变量,可以合理地得出结论,_aimageView可能是一个概念上由当前对象"拥有"的图像视图,而onSuccess是一个由"拥有"的完成块。图像视图。除非某些其他对象具有强引用,否则"拥有"对象的生命周期仅限于其"拥有"对象的生命周期。因此,块不会比图像视图寿命更长,图像视图不会比当前对象寿命更长。

你可能会害怕的唯一方法(当块运行时,self指向的对象被释放)可能会发生,如果某个其他对象存储对_aimageView或块的强引用。对于块,一个对象的"成功块"不太可能与其他对象一起存储。对于图像视图,如果图像视图"属于"当前对象,则同样不太可能。 (可以想象它可以由自动释放池或其他东西存储;但是自动释放池除了释放之外不会调用它上面的东西,所以这不是问题。)

我能想到的唯一例外是图像视图是由挂起的网络操作还是其他东西保留的,当它完成时会调用onSucess块。但如果是这种情况,我会说将单个对象同时用作视图和网络操作是不好的设计。相反,在这种情况下,应该有一个专用的网络操作对象,它是一个局部变量,并在其上设置一个完成块(例如在图像视图中存储图像,设置标签等),启动操作,不需要存储操作。然后块可以强烈引用self,但没有保留周期。

总而言之,块(以及拥有它的图像视图)应该分为两类,基于谁保持它活着(即谁保留它,谁保持它的强引用):

  • 如果self保持活着:例如视图控制器使其视图和子视图保持活动状态。在这种情况下,使用__weak是安全的,因为块在self的生命周期之外不存在。

  • 如果其他人保持活着:例如警报视图 - 您只需创建并显示它;之后系统会将其保留在屏幕上。在这种情况下,self不应该(也不需要)引用它,并且可以在块中强烈引用self;它不会导致保留周期。

尽量避免self和其他人保持活着的情况。


一个块可以提到self而不会导致任何泄漏。所以要做的第一件事是指定情况。为什么你认为你的块泄漏了?你检查过吗?如果没有理由担心,没有必要担心。

如果块提到self确实导致泄漏,那么您可以采取的步骤取决于发生这种情况的原因。但是你没有告诉我们。例如,提到self的调度计时器块可能会导致保留周期,但是当您完成计时器后,您可以打破周期,因此没有泄漏。

因此,最好是理解一个特定的保留周期,而不是在不必要的时候对每个块进行防御性编程。

编辑(回应你的评论)我的书中的代码演示了在NSNotification观察者情况下处理内存管理的各种方法:点击这里


1
2
3
4
5
6
7
-(void)someMethod{
    AnotherObject *abc=[[AnotherObject alloc]init];
    abc.onSuccess=^{
        [self method2];
        [self method3];
    }
}

这将使。它不会导致保留周期。


由于您需要保证执行method2method3,因此您需要self引用的对象保持活动状态。为此,请遵循标准模式:只要您需要,请保留强大的参考。你的第二个代码片段做到了。

参考周期本身并不坏,它是非托管的。在这里你做一个循环,然后打破它,所以没有问题。

(注意,因为你可以向nil发送消息,如果strongSelfnil,第一个代码片段不会崩溃 - 它只是不会做任何事情。)