1

I have a method with a callback that looks something like this:

- (void)doStuff:(void ^())callback
{
    //Do a whole bunch of stuff

    //Perform callback
    callback();
}

I would then call this method later on like this:

[self doStuff:^{[self callbackMethod];}];

This works just fine when there is no data to pass, but now I have some data that I need to pass between the methods.

Take the following method:

- (void)showAViewWithOptions:(int)options

In this method, I show a view with certain options, but if there's something else already on the screen, I call the method to hide it with a callback back to this method.

So the implementation looks like this.

- (void)hideOldView:(void ^())callback
{
     //Hide all objects in _oldViews and set _oldViews = nil

     callback();
}

- (void)showAViewWithOptions:(int)options
{
     if(_oldViews != nil)
     {
         [self hideOldView:^(int options){[self showAViewWithOptions:options];}];
         return;
     }

     //Show the new view
}

This compiles and runs without issue, but options loses its value after being passed.

Quite frankly, it surprised me that it compiled, since I thought it wouldn't accept a block with arguments.

For instance, if I call [self showAViewWithOptions:4];, when the callback is fired, options = -1730451212.


How do I bind the value options to the block? Or a better question, is this simply not possible because when I call the callback:

callback();

I'm not putting anything into the parentheses?

If so, then a good follow-up question would be: why does this even compile in the first place?

David
  • 21,070
  • 7
  • 57
  • 113
  • @trojanfoe but where exactly? I know in JS, you can pass a parameter to a block by binding it at the end like so: `function(parameter){//Do stuff}(boundVariable);` but I don't know how to do something like this in Objective C. – David Nov 12 '15 at 09:27
  • I take that comment back. I've provided an answer and believe it will work. – trojanfoe Nov 12 '15 at 09:28

3 Answers3

3

This should work:

- (void)showAViewWithOptions:(int)options
{
     if(_oldViews != nil)
     {
         [self hideOldView:^(){
             // Recursion doesn't feel right; be careful!
             // Why can't whatever is being done by this call be done
             // within this block?
             [self showAViewWithOptions:options];
         }];
         return;
     }

     //Show the new view
}
trojanfoe
  • 116,099
  • 18
  • 197
  • 233
  • Oh really? I don't have to bind it? It just carries directly through? – David Nov 12 '15 at 09:28
  • No. Blocks have access to all variables within their scope at the time they are created. – trojanfoe Nov 12 '15 at 09:29
  • Would you look at that. It works. Wow. I figured it would be very similar to the way Javascript handles blocks. My mistake. Thanks! – David Nov 12 '15 at 09:30
  • So quick question, if you know the answer: why does passing a `^(int)` block to a `^()` block compile if they have different numbers and types of parameters? – David Nov 12 '15 at 09:31
  • @David I'm not sure about that TBH. I am surprised the compiler allowed it. – trojanfoe Nov 12 '15 at 09:31
  • Alright well thanks for the answer to my problem! I'll mark it in a sec. – David Nov 12 '15 at 09:32
  • @David OK cheers; I am a bit wary of this recursion however; look out for that... – trojanfoe Nov 12 '15 at 09:32
  • I handle it better than I did in my post, but I didn't want to post all of the other unrelated code. – David Nov 12 '15 at 09:33
  • @David OK, I've added a comment to my code so my complaint is officially logged ;-) – trojanfoe Nov 12 '15 at 09:35
  • What is done inside that block is used in multiple places, and I need there to be a time pause while the other code is executed; there's a transition period that I want to complete before I execute all of the code below. Don't worry, the recursion is all thoroughly checked at each entry and exit point to make sure that there are no infinite loops. – David Nov 12 '15 at 09:36
  • OK that's cool. It's a slightly odd pattern for blocks in my experience. – trojanfoe Nov 12 '15 at 09:37
  • It's just a slightly out-of-the-box way to aggregate my code into one method. I could be firing this method from any number of different states, so at any point, I know that all I have to do is call this one method to move to a different state of the application, if that makes sense. It's just a way to make sure that I only need one method instead of (2 * number of states that call it)...it's a little confusing. – David Nov 12 '15 at 09:40
  • 1
    @David: `^(int)` to `^()` compiles because in C, `()` in a parameter list really means "anything goes". It is a historical artifact. [`(void)` means "no parameters please"](http://stackoverflow.com/questions/693788/c-void-arguments). – Jesper Nov 12 '15 at 09:58
  • Note that the `()` is optional in the block literal when there are no arguments – newacct Nov 17 '15 at 06:57
0

A block with a return value and parameters looks like this:

^ return_type (parameter1_type parameter1_name, parameter2_type parameter2_name, ...) {
do_stuff;
};
Laserbeak
  • 78
  • 5
0

you can pass vairable into method... Callback method you call inside method:

- (void)hideOldViewWithId:(float)f callback:(void (^)(float f))callback{
    f = f + 2.0f;
    callback(f);
}

and then call

[self hideOldViewWithId:1.0f callback:^(float f) {
    NSLog(@"callback with float: %f", f);
}];
Jiri Zachar
  • 459
  • 3
  • 7