Friday, May 25, 2012

Blocks (6)

I decided to work up two more examples from Mike Ash's Friday Q&A about blocks.

Both examples involve a callback. Your program sets up a situation where you'd like to defer the execution of some code to a later time. The classic example is a file dialog. You're asking the user for input, but don't necessarily halt the program until he makes up his mind. Another one is where you have a bunch of calculations that could be done concurrently. I still have to work up a good example of that.

Here we have a notification in the first example, and a timer in the second. The implementations use a category on NSObject. This works because a block is an Objective-C object. An alternative approach would be to use a function rather than a method.

The examples may look a bit strange. The block object is added as the observer, and its method my_callBlockWithObject or my_CallBlock is called when the notification appears or the timer finishes. It looks strange because in the method, the block object is obtained with self and then called.

Notice the use of [block copy].

// clang blocks.m -o prog -framework Foundation -fobjc-gc-only
#import 

typedef void (^B)(NSNotification *note);
B b = ^(NSNotification *note){ NSLog(@"Note!"); };

@implementation NSObject (BlocksAdditions)

- (void)my_callBlockWithObject:(id)arg {
    B b = (id)self;
    b(arg);
}
@end

@implementation NSNotificationCenter (BlocksAdditions)
    
- (void)addObserverForName: (NSString *)name 
                    object: (id)obj
                     block: (B)b {
                         
    [self addObserver: [b copy] 
             selector: @selector(my_callBlockWithObject:)
                 name: name 
               object: obj];
}
@end

int main(int argc, char * argv[]) {
    NSNotificationCenter *cent = [NSNotificationCenter defaultCenter];
    [cent addObserverForName: @"MyNotification"
                      object: nil
                       block: b];
                                                       
    [cent postNotificationName:@"MyNotification" object:nil];
    return 0;
}


> ./prog2012-05-21 07:40:28.601 prog[3441:707] Note!


// clang blocks.m -o prog -framework Foundation -fobjc-gc-only
#import 

typedef void (^B)();

@implementation NSObject (BlocksAddition)
- (void) my_CallBlock {
    B b = (id)self;
    b();
}
@end

void RunAfterDelay(NSTimeInterval delay, B b) {
    [[b copy] performSelector:@selector(my_CallBlock)
                   withObject:nil
                   afterDelay:delay ];
}

int main(int argc, char * argv[]) {
    NSString *s = @"world!";
    RunAfterDelay(0, ^{ NSLog(@"Hello %@", s);  });
    
    NSRunLoop* rl = [NSRunLoop currentRunLoop];
    [rl runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
    return 0;
}

> ./prog
2012-05-21 07:41:00.457 prog[3455:707] Hello world!