3

I love using blocks. I constantly create custom classes that use blocks to communicate with callers instead of delegate mechanisms.

But the problem is that these classes got polluted with checks to see if the block was declared before running them. Something like:

if (self.onExit) {
  self.onExit(flag);
}

obviously I cannot omit the if or the code will crash if _onExit is nil;

Is there something I can create, a category or something, that will allow me to just run the block directly without the thousands of ifs but will internally check for nil before running it?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Duck
  • 34,902
  • 47
  • 248
  • 470
  • Perhaps you should consider migrating to swift and use a much shorter `?` unwrapping :) – alexburtnik Oct 29 '16 at 19:03
  • Switch to Swift. In Swift blocks are known as closures, and a closure that can be nil is enclosed in an Optional. You can resolve an optional with a question mark. So you'd say `self.onExit?()`. The question mark means "check the closure to see if it's nil. If yes, stop. If it's not nil, call it. – Duncan C Oct 29 '16 at 19:03
  • unfortunately I cannot migrate this code to swift because this is a project for someone else. – Duck Oct 29 '16 at 19:21
  • 1
    Why do you have thousands of `if`s for this one property? Why don't you wrap their use in a helper method? Then use the helper method and only have one `if` (inside the helper method). – rmaddy Oct 29 '16 at 20:25
  • @maddy - because this is called from several other viewcontrollers. – Duck Oct 29 '16 at 20:35
  • I think rmaddy's saying something like this: https://gist.github.com/woolsweater/d90584352833f6b0a8d7414479321eee – jscs Oct 29 '16 at 20:53
  • 1
    @JoshCaswell Exactly. Nice. – rmaddy Oct 30 '16 at 00:13
  • @SpaceDog Unrelated FYI - when you wish to reply to someone, type `@` and the first letter of their username. A tooltip of matching users currently involved in the question/comments will appear. Press tab to complete the username automatically. If no tooltip appears then the username you are trying to use is incorrect. I didn't see your last reply because my username is not `maddy`, it's `rmaddy`. Enjoy. – rmaddy Oct 30 '16 at 00:15

2 Answers2

3

What about a macro like this?

#define BLOCK_SAFE_RUN(block, ...) block ? block(__VA_ARGS__) : nil

This is from another answer with more details here: https://stackoverflow.com/a/13037198/747339

Community
  • 1
  • 1
vievievie
  • 328
  • 3
  • 10
2

rmaddy suggested this in a comment and I followed up with an example: a little bit of refactoring might be the way to go here. Put the null-check-and-call into a method. Then call the method unconditionally wherever you have the conditional call of the Block.

Something like this:

@interface MyClass : NSObject
@property (copy, nonatomic) void (^onExit)(BOOL);
@end

@interface MyClass ()
- (void)performExit:(BOOL)flag;
@end

@implementation MyClass

- (void)performExit:(BOOL)flag
{
    if( self.onExit ){
        self.onExit(flag);
    }
}

- (void)doThatThing
{
    // Replace
    // if( self.onExit ){
    //     self.onExit(flag);
    // }
    // with
    [self performExit:YES];
    // everywhere.
}

@end
Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195