Evgeniy Kleban
Evgeniy Kleban

Reputation: 6965

Properly using blocks for callbacks between two classes

I have simple app with two views: the first view has a button, and when you press it you get a modal segue to another view. As you know, the controller popped out with a modal segue doesn't have a "Back" button, so you probably write delegates. I want to use blocks for more compact code. Firstl I did the following.

Declare block in "Second" view controller:

typedef void (^SecondViewControllerCompletionBlock)(BOOL success);

@interface SecondViewController : UIViewController

@property (nonatomic, copy) SecondViewControllerCompletionBlock CompletionBlock;

I wrote code in a "Done" button's action (which is the button on top of the UINavigationController):

- (IBAction)doneBarButton:(id)sender {

    if (self.CompletionBlock !=nil){
        self.CompletionBlock (YES);
    }

}

Preparing in the "First" view controller for segue:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{



    if ([segue.identifier isEqualToString:@"ShowDetail"]){

     __weak SecondViewController *controller = segue.destinationViewController;
     controller.CompletionBlock = ^(BOOL success){

         if (NO){
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    };

}

So, everything works just fine; I can close the menu by pressing the done button. But what if I want something other than BOOL when declaring the block? I tried using an int.

Declaring the block:

typedef void (^SecondViewControllerCompletionBlock)(int success);

@interface SecondViewController : UIViewController

@property (nonatomic, copy) SecondViewControllerCompletionBlock CompletionBlock;

Changing the code in the doneBarButtonItem: action method:

- (IBAction)doneBarButton:(id)sender {

    if (self.CompletionBlock !=nil){
        self.CompletionBlock (1);
    }

}

In this method when I wrote an if statement, I placed 2 in round brackets as the argument, but, my point was, if you put here anything except 1 (which I pass when configuring the action method), the statement should not execute, so, controller should not close. But it does, in all cases, if I put 1 in if(1) statement, or 2, or anything else.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{

    if ([segue.identifier isEqualToString:@"ShowDetail"]){

        __weak SecondViewController *controller = segue.destinationViewController;
        controller.CompletionBlock = ^(int success){

            if (2){
               [self dismissViewControllerAnimated:YES completion:nil];
            }
        };

}

I might be missing something obvious. Can you help me fix this? It would be nice to have not only BOOL for changing actions.

Upvotes: 1

Views: 257

Answers (1)

Gabriele Petronella
Gabriele Petronella

Reputation: 108169

if (2) {
    ...
}

makes little sense. In C (and Objective-C) any non-zero value evaluates to true, so the test you are making is roughly equivalent to

if (YES) {
    ...
}

which, obviously, always passes.

What you really want is to test the argument against that value:

controller.CompletionBlock = ^(int success){
    if (success == 2){
        [self dismissViewControllerAnimated:YES completion:nil];
    }
};

Finally, a stylistic remark: all variables should start with a lowercase letter and block variables make no exception. CompletionBlock should be named completionBlock, instead.

Upvotes: 1

Related Questions