1

Is it possible to define a block after passing it to a method? I want to do this so the code is in somewhat the order it runs in:

// Declare the block
void (^doStuffBlock)(void);

// Pass the block.
[self prepareToDoStuffWithCompletion:doStuffBlock];

// Define the block.
doStuffBlock = ^void() {
  // Do stuff
};

doesn't work because inside prepareToDoStuffWithCompletion: the block doStuffBlock is nil.

richy
  • 2,716
  • 1
  • 33
  • 42

4 Answers4

0

you should first define the block then pass it to the method:

// Declare the block
void (^doStuffBlock)(void);

// Define the block.
doStuffBlock= ^void() {
  // Do stuff
};

// Pass the block.
[self prepareToDoStuffWithCompletion:doStuffBlock];
BoygeniusDexter
  • 2,154
  • 1
  • 16
  • 14
  • I understand this way, i was asking if it was possible at all to define `doStuffBlock` after calling `prepareToDoStuffWithCompletion:doStuffBlock` – richy Apr 19 '21 at 14:46
0

You could use a typedef.

typedef void (^TypeName)(void);
- (void)bar:(TypeName)completion {
   completion();
}

TypeName foo = ^() { /*...*/ };

[self bar:foo]; 

(My obj-c syntax might be a little rusty, but what you want to do is possible in both Objective-C and Swift.

https://stackoverflow.com/a/29580490/620197

http://goshdarnblocksyntax.com/

Rob
  • 415,655
  • 72
  • 787
  • 1,044
Mike D
  • 4,938
  • 6
  • 43
  • 99
  • I was wanting to define the block after passing it i.e. this line `TypeName foo = ^() { /*...*/ };` to somehow go after this line `[self bar:foo]; ` – richy Apr 19 '21 at 14:45
0

If you supply the completion handler closure, you are effectively saying “here is the closure I want you to use”.

If you are going to supply the closure later you would probably define a property:

@property (nonatomic, copy, nullable) void (^doStuff)(void);

Then, do not supply the closure when you call the method, but rather refer to this property:

- (void)prepareToDoStuff {
    [self somethingAsynchronousWithCompletion:^{
        if (self.doStuff) {
            self.doStuff();

            // if completion handler, you’d often release it when done, e.g. 
            //
            // self.doStuff = nil;
        }
    }];
}

And, then you can call this method and supply the closure later:

[self prepareToDoStuff];

self.doStuff = ^{
    NSLog(@"do stuff done");
};

A few additional considerations:

  1. Make sure you synchronize your access to this doStuff property. E.g., in the above, I am assuming that the somethingAsynchronousWithCompletion is calling its completion handler on the main thread. If not, synchronize your access (like you would any non-thread-safe property in a multithreaded environment).

  2. There is a logical race if you first call the method that will eventually call the block, and only later set that block property. Sometimes that is perfectly fine (e.g. maybe you are just trying to specify what UI to update when the asynchronous process finishes). Other times, the race can bite you. It depends upon the functional intent of the block property.

  3. I would give the block property a name that better reflects its functional purpose (e.g. completionHandler or notificationHandler or didReceiveValue or whatever).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

If you are certain that the method will run your doStuffBlock after you "define" it, what you could do is have your doStuffBlock capture a variable holding a second block with the real logic of what it should do. You can set the real-logic block after you create doStuffBlock, and you need to make sure that the variable holding the real-logic block is a __block variable, so that changes to the variable in the function scope are seen in the block scope.

__block void (^realLogicBlock)(void);

[self prepareToDoStuffWithCompletion:^{
  if (realLogicBlock)
    realLogicBlock();
}];

realLogicBlock = ^void() {
  // Do stuff
};

You might have to be careful about retain cycles though -- if inside realLogicBlock, you capture a reference to self or to something that will reference the prepareToDoStuffWithCompletion: completion handler, you would have a retain cycle, in which case you may have to introduce a weak reference somewhere.

newacct
  • 119,665
  • 29
  • 163
  • 224