Search code examples
iosobjective-cmethodspass-by-referenceblock

Obj-C Blocks as Parameter


I have a ErrorBlock (errorBlock) defined in my code that can be called multiple times. Though I only want to be informed once about the call, so my idea was to wrap my ErrorBlock (errorBlock) inside another (pErrorBlock) and set that one nil.

Now I wanted to have a cleanup method that sets the references of the innerBlock to nil. But when i hand the inner block over to the cleanup method (Test 1), I have a different result then when I set the inner ErrorBlock nil self (Test 2).

How can I get the result of Test 2, with (a slightly modified) code of Test 1?

My frist Test, shoes a result I don't understand:

-(void)testBlocks{
    __block ErrorBlock pErrorBlock= ^(void){
        NSLog(@"Foo");
    };
    NSLog(@"test: %p",pErrorBlock);

    ErrorBlock errorBlock = ^(void){
        dispatch_async(dispatch_get_main_queue(), ^{
            if(pErrorBlock){
                NSLog(@"call: %p",pErrorBlock);
                pErrorBlock();
            }

            [self cleanupErrorBlockWith:pErrorBlock];
        });
    };

    errorBlock();
    errorBlock();
}

- (void) cleanupErrorBlockWith:(ErrorBlock)errorBlock{

    NSLog(@"cleanup: %p",errorBlock);
    errorBlock = nil;
    NSLog(@"after cleanup: %p",errorBlock);
}

Result:

2013-02-08 16:39:04.484 Tests[9501:907] test: 0x3a6ba8
2013-02-08 16:39:04.496 Tests[9501:907] call: 0x3a6ba8
2013-02-08 16:39:04.497 Tests[9501:907] Foo
2013-02-08 16:39:04.498 Tests[9501:907] cleanup: 0x3a6ba8
2013-02-08 16:39:04.499 Tests[9501:907] after cleanup: 0x0
2013-02-08 16:39:04.500 Tests[9501:907] call: 0x3a6ba8
2013-02-08 16:39:04.501 Tests[9501:907] Foo
2013-02-08 16:39:04.502 Tests[9501:907] cleanup: 0x3a6ba8
2013-02-08 16:39:04.503 Tests[9501:907] after cleanup: 0x0

My second Test, with the desired result is:

-(void)testBlocks{
    __block ErrorBlock pErrorBlock= ^(void){
        NSLog(@"Foo");
    };
    NSLog(@"test: %p",pErrorBlock);

    ErrorBlock errorBlock = ^(void){
        dispatch_async(dispatch_get_main_queue(), ^{
            if(pErrorBlock){
                NSLog(@"call: %p",pErrorBlock);
                pErrorBlock();
                NSLog(@"cleanup: %p",pErrorBlock);
                pErrorBlock = nil;
                NSLog(@"after cleanup: %p",pErrorBlock);
            }
        });
    };

    errorBlock();
    errorBlock();
}

Result:

2013-02-08 16:42:18.485 Tests[9540:907] test: 0x3a6ba8
2013-02-08 16:42:18.496 Tests[9540:907] call: 0x3a6ba8
2013-02-08 16:42:18.498 Tests[9540:907] Foo
2013-02-08 16:42:18.499 Tests[9540:907] cleanup: 0x3a6ba8
2013-02-08 16:42:18.500 Tests[9540:907] after cleanup: 0x0

Solution

  • You are passing block by value, pass it by reference like this. Also moving you cleanupErrorBlockWith: call inside if statement.

    -(void)testBlocks{
        __block ErrorBlock pErrorBlock= ^(void){
            NSLog(@"Foo");
        };
        NSLog(@"test: %p",pErrorBlock);
    
        ErrorBlock errorBlock = ^(void){
            dispatch_async(dispatch_get_main_queue(), ^{
                if(pErrorBlock){
                    NSLog(@"call: %p",pErrorBlock);
                    pErrorBlock();
                    [self cleanupErrorBlockWith:&pErrorBlock];
                }
            });
        };
    
        errorBlock();
        errorBlock();
    }
    
    
    - (void) cleanupErrorBlockWith:(ErrorBlock*)errorBlock{
        NSLog(@"cleanup: %p",*errorBlock);
        *errorBlock = nil;
        NSLog(@"after cleanup: %p",*errorBlock);
    }