Objective-C Blocks, Recursion Fails

Sup guys,

I’m trying to do a function that calls itself but by putting everything on one block,

  • ARC, self and blocks
  • Storing Blocks in an Array
  • Do we need to use __weak self inside UIAnimationBlocks in ARC?
  • Difference between block (Objective C) and closure (Swift) in ios
  • How do I avoid capturing self in blocks when implementing an API?
  • AFHTTPRequestOperationManager return data in block
  • As you can see, the following function is intended to be called an indefinite amount of times (until arcrandom returns a number lower than 50) and you should expect as an output a variable number of “RUNNING” messages, depending on chance.

    void (^_test_closure)(void) = ^ {
        NSLog(@"RUNNING");
        if(arc4random() % 100 > 50) {
            _test_closure();
        }
    };
    
    _test_closure();
    

    However, when running it, I get an EXC_BAD_ACCESS error and the reason I’ve found is that when the code tries to calls _test_closure inside of the closure it basically points to nowhere.

    Does anyone know how to make the above code work?

    3 Solutions Collect From Internet About “Objective-C Blocks, Recursion Fails”

    Recursion and blocks is tricky. Because a block captures all variables passed in, the variable _test_closure is not initialized yet (and clang should give you a warning:

    Block pointer variable ‘_test_closure’ is uninitialized when captured by block

    ).

    There are several ways you can get around this, but the most obvious & simplest is to just make the block itself a __block variable (what @H2CO3 said). This allows the block to be weak-linked almost, so that when you call it again, it is properly initialized.

    Another option you have is making the block a global or static, like this:

    // outside of 'main', thus being a global variable
    void (^blockRecurse)(int) = ^(int level) {
        if (level < 0)
            return;
        NSLog(@"Level: %i", level);
        blockRecurse(--level);
    };
    
    int main()
    {
        @autoreleasepool {
            blockRecurse(10);
        }
    } 
    

    This means it’s not being captured by the block, but instead it’s referencing the global / static variable, which can be changed by all code equally.

    You have to declare your block itself as a block variable:

    __block void (^_test_closure)();
    
    
    _test_closure = ^{
        NSLog(@"Running...");
        if ((arc4random() % 100) > 50) {
            _test_closure();
        }
    }
    
    _test_closure();
    

    It works with XCode 5 – no warnings, no retain cycles:

    typedef void(^blockT)();
    
    blockT block1;
    blockT __block block1recursive;
    
    block1recursive = block1 = ^(){
        block1recursive();
    };
    
    block1();