문제

First of all (and since this question has to do with memory management), I have to say I'm running on ARC.

I have an object (MyObject) which contains an array of MyProcess objects. MyObject, at a certain point, creates a new MyProcess, adds it to the array, gives it a completion handler in the form of a block, and then tells the process to start.

MyProcess* newProcess = [MyProcess new];

[allProcessesArray addObject: newProcess];

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

[newProcess start];

Now, on MyProcess side of things, when start is called, MyProcess internally calls threadedStart (that runs on a background thread) which does its work and then calls the block when finished:

- (void)threadedStart
{
    // Do something

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

The completion block is defined as a property in the interface of MyProcess in this way:

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

Now, MyProcess is only kept alive during its lifetime by allProcessesArray, since that array is the only object that has a reference to the process. When on the completion block I remove the process from the array, I reckon the process is immediately deallocked. Then, since the process contains the block itself, the block gets deallocked as well while it is still running!

I would expect this to cause problems, but I've tested this code and the block runs fine until the end. Now, there are two options: either my reasoning is wrong and this code is perfectly safe, or I'm right (or at least partially right) and this isn't safe as it only works sometimes.

Basically, my question is: is this approach to a callback block safe? And if not, can you suggest something different?

도움이 되었습니까?

해결책

For your block property, it should be a copy, not a strong. Blocks should always be copied.

@property (copy) MyCallbackBlock completionBlock;

Whether running your block causes problems is based on what code is run and how the variables that it uses are defined.

Inside your block, because of the way myProcess is defined outside of the block, it's retained inside the block. So, when you remove it from the array it is still being retained until the end of the block.

Technically this could be classed as a form of retain cycle, but it works for you because of its structure.

The block itself can't be "dealloc'd" while it's running.


For your updated code, the instance is now (and probably was before, I just didn't look) retained by the call to the block (because it is supplied as a parameter). Again, the instance will be retained until the end of the block execution.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top