2

首先(由于这个问题与内存管理有关),我不得不说我在 ARC 上运行。

我有一个包含 MyProcess 对象数组的对象 (MyObject)。MyObject 在某个时刻创建一个新的 MyProcess,将其添加到数组中,以块的形式为其提供完成处理程序,然后告诉进程开始。

MyProcess* newProcess = [MyProcess new];

[allProcessesArray addObject: newProcess];

newProcess.completionBlock = ^(MyProcess* process){
    [allProcessesArray removeObject: process];
    // Other things are done here
};

[newProcess start];

现在,在 MyProcess 方面,当调用 start 时,MyProcess 在内部调用 threadedStart (在后台线程上运行),它完成它的工作,然后在完成时调用块:

- (void)threadedStart
{
    // Do something

    dispatch_async(dispatch_get_main_queue(), ^{ self.completionBlock(self); });
}

完成块在 MyProcess 的接口中定义为一个属性,方式如下:

typedef void(^MyCallbackBlock)(MyProcess* process);
@property (strong) MyCallbackBlock completionBlock;

现在,MyProcess 仅在其生命周期内由 allProcessesArray 保持活动状态,因为该数组是唯一引用该进程的对象。当在完成块上我从数组中删除进程时,我认为进程会立即解除锁定。然后,由于进程包含块本身,块在它仍在运行时也会被解除锁定!

我预计这会导致问题,但我已经测试了这段代码,并且该块运行良好,直到结束。现在,有两种选择:要么我的推理是错误的,而这段代码是完全安全的,要么我是对的(或至少部分是对的),但这是不安全的,因为它只在某些时候有效。

基本上,我的问题是:这种回调块的方法安全吗?如果没有,你能提出一些不同的建议吗?

4

1 回答 1

1

对于您的块属性,它应该是 a copy,而不是 a strong。块应该总是被复制。

@property (copy) MyCallbackBlock completionBlock;

运行块是否会导致问题取决于运行的代码以及它使用的变量是如何定义的。

在您的块内,由于在myProcess块外定义了方式,因此将其保留在块内。因此,当您从数组中删除它时,它仍然会保留到块的末尾。

从技术上讲,这可以归类为保留循环的一种形式,但由于其结构,它对您有用。

块本身在运行时不能“dealloc'd”。


对于您更新的代码,该实例现在(可能是以前,我只是没看)由对块的调用保留(因为它是作为参数提供的)。同样,实例将一直保留到块执行结束。

于 2013-06-23T11:13:51.823 回答