1

I have two blocks declared as local variables. A networking block that calls a retry block on error, and a retry block that calls the networking block.

The retry block is called in several different error circumstances, hence using a block to eliminate duplicated code.

However, this scenario does not work due to the source code order of the declared blocks.

void (^upperBlock)() = ^{

    // variable is used before it is declared.
    lowerBlock(); // Error: implicit declaration of function `lowerBlock` is invalid in C99
};

void (^lowerBlock)() = ^{
    upperBlock(); // ok!
};

It does not work to give the lower block a forward declaration, as the upper block captures the initial value of the forward declaration before the variable is reassigned (even if it is called later).

void (^lowerBlock)() = nil;

void (^upperBlock)() = ^{
    lowerBlock(); // crash! block is `nil`
};

lowerBlock = ^{
    // new value not assigned, despite `upperBlock` being *called* below this point
};

upperBlock()

I could use __block on the lowerBlock, in which case the upperBlock will call the freshly assigned version of the variable. But using __block seems like overkill for this scenario if another solution is possible.

Is there any inline, block-as-a-local-variable solution to allow the upper and lower blocks to reference each other?

pkamb
  • 33,281
  • 23
  • 160
  • 191

1 Answers1

1

Captures are by value with Blocks unless the variable is marked as __block. That's the correct solution here.

If you declare lower as a __block variable, it will be captured as a reference rather than taken as a copy, and the upper will see its "current" value when called:

__block BOOL proceedWithInfiniteRegress = YES;
__block dispatch_block_t lower;

dispatch_block_t upper = ^{
    if( proceedWithInfiniteRegress ){
        lower();
    }
};

lower = ^{
    proceedWithInfiniteRegress = NO;
    NSLog(@"Hello, reference cycle!");
    upper();
};

upper();
jscs
  • 63,694
  • 13
  • 151
  • 195
  • If `__block` is the only way I suppose I'll use that. Just seems inelegant when used only as a way to get around top-to-bottom lines of code scope. – pkamb Apr 26 '17 at 01:21
  • 1
    ObjC is following the rules of C here. The Block literally gets rewritten to a `struct`: http://stackoverflow.com/a/17819142 You have to use a layer of indirection inside to do what you want. Another option would be for the Blocks to get at each other via some object's property accessors. But that would just be a bunch of glue code. – jscs Apr 26 '17 at 01:30
  • It certainly does, yes. – jscs Apr 29 '17 at 11:54