fabian789
fabian789

Reputation: 8412

Do I need to copy a block if I use it in the method it was passsed to?

I have a method similar to this:

- (void)loadStoreWithCompletion:(CompletionBlock)loadCompletion
{
    dispatch_queue_t loadQueue = dispatch_queue_create("loadQueue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(loadQueue, ^{

        // ... Do background stuff ...
    });

    dispatch_async(loadQueue, ^{

        dispatch_async(dispatch_get_main_queue(), ^{

            loadCompletion();
        });
    });

    dispatch_release(loadQueue);
}

Do I need to copy the loadCompletion block because it only might get called when the caller of this method no longer exists, or is it fine like this?

Upvotes: 0

Views: 133

Answers (4)

Jack
Jack

Reputation: 133567

Blocks are allocated on the stack by the caller, this means that once out of scope, an invocation leads to an error.

You should always copy them to the heap when you intend to use them later (as in a callback which happens asynchronously), you can do it either

  • by directly invoking [block copy]
  • by storing them in a property which is declared as copy

Mind that retaining them or releasing them doesn't always work because they might be on stack.

Upvotes: 0

Ken Thomases
Ken Thomases

Reputation: 90531

You can rely on dispatch_async() doing the right thing with the blocks that are passed to it directly. It's also the case that when a block is copied, it copies any blocks which are captured by it. Since loadCompletion is referenced by the inner block and it is not declared with __block, it is captured. In other words, you're fine.

Upvotes: 3

ipmcc
ipmcc

Reputation: 29886

The act of referencing the block parameter inside another block will cause it to be copied to the heap implicitly, so you don't need to explicitly do so.

Upvotes: 3

Wolfgang Schreurs
Wolfgang Schreurs

Reputation: 11834

It's fine like this. But you should make 1 change in your code:

dispatch_async(loadQueue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (loadCompletion) {
            loadCompletion();       
        }
    });
});

You can test it yourself. If you call the method without any parameters ([instance loadStoreWithCompletion:nil];), in your version of the code the app would crash upon invoking the completion handler. The block check will prevent any crashes in case the block does not exist (anymore).

Upvotes: 0

Related Questions