Rambo Li Rambo Li - 6 months ago 27
Objective-C Question

How to implement a NSNotificationCenter (block style)?

It's an interview question. I know how to implement a non-block Notification Center with NSDictionary. But I have no idea how to implement a block NSNotification center because there is no "observer". Here is the code. Any idea?

#import <Foundation/Foundation.h>

/** LBNotificationCenter.h */

typedef void (^Observer)(NSString *name, id data);

@interface LBNotificationCenter : NSObject

- (void)addObserverForName:(NSString *)name block:(Observer)block;
- (void)removeObserverForName:(NSString *)name block:(Observer)block;
- (void)postNotification:(NSString *)name data:(id)data;

@end

/** LBNotificationCenter.m */

@interface LBNotificationCenter ()

@end

@implementation LBNotificationCenter

- (instancetype)init
{
self = [super init];
if (self) {

}
return self;
}

- (void)addObserverForName:(NSString *)name block:(Observer)block
{
//add your code here
}

- (void)removeObserverForName:(NSString *)name block:(Observer)block
{
//add your code here
}

- (void)postNotification:(NSString *)name data:(id)data
{
//add your code here
}

Answer

Blocks are normal objects so you can store them in NSArray/NSDictionary. Having that said the implementation is straightforward.

#import <Foundation/Foundation.h>

/** LBNotificationCenter.h */

typedef void (^Observer)(NSString *name, id data);

@interface LBNotificationCenter : NSObject

- (void)addObserverForName:(NSString *)name block:(Observer)block;
- (void)removeObserverForName:(NSString *)name block:(Observer)block;
- (void)postNotification:(NSString *)name data:(id)data;

@end

/** LBNotificationCenter.m */

@interface LBNotificationCenter ()
@property (strong, nonatomic) NSMutableDictionary <id, NSMutableArray <Observer> *> *observers;
@end

@implementation LBNotificationCenter

- (instancetype)init
{
    self = [super init];
    if (self) {
        _observers = [NSMutableDictionary new];
    }
    return self;
}

- (void)addObserverForName:(NSString *)name block:(Observer)block
{
    // check name and block for presence...

    NSMutableArray *nameObservers = self.observers[name];

    if (nameObservers == nil) {
        nameObservers = (self.observers[name] = [NSMutableArray new]);
    }

    [nameObservers addObject:block];
}

- (void)removeObserverForName:(NSString *)name block:(Observer)block
{
    // check name and block for presence...

    NSMutableArray *nameObservers = self.observers[name];

    // Some people might argue that this check is not needed
    // as Objective-C allows messaging nil
    // I prefer to keep it explicit
    if (nameObservers == nil) {
        return;
    }

    [nameObservers removeObject:block];
}

- (void)postNotification:(NSString *)name data:(id)data
{
    // check name and data for presence...

    NSMutableArray *nameObservers = self.observers[name];

    if (nameObservers == nil) {
        return;
    }

    for (Observer observer in nameObservers) {
        observer(name, data);
    }
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *const Notification1 = @"Notification1";
        NSString *const Notification2 = @"Notification2";

        LBNotificationCenter *notificationCenter = [LBNotificationCenter new];

        Observer observer1 = ^(NSString *name, id data) {
            NSLog(@"Observer1 is called for name: %@ with some data: %@", name, data);
        };
        Observer observer2 = ^(NSString *name, id data) {
            NSLog(@"Observer2 is called for name: %@ with some data: %@", name, data);
        };

        [notificationCenter addObserverForName:Notification1 block:observer1];
        [notificationCenter addObserverForName:Notification2 block:observer2];

        [notificationCenter postNotification:Notification1 data:@"Some data"];
        [notificationCenter postNotification:Notification2 data:@"Some data"];

        [notificationCenter removeObserverForName:Notification1 block:observer1];

        // no observer is listening at this point so no logs for Notification1...
        [notificationCenter postNotification:Notification1 data:@"Some data"];

        [notificationCenter postNotification:Notification2 data:@"Some data"];
    }

    return 0;
}