├── .gitignore ├── MANotificationCenter.h ├── MANotificationCenter.m ├── README.markdown └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | -------------------------------------------------------------------------------- /MANotificationCenter.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | 5 | @interface MANotificationCenter : NSObject 6 | { 7 | NSMutableDictionary *_map; 8 | } 9 | 10 | - (id)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block; 11 | - (void)removeObserver: (id)observer; 12 | 13 | - (void)postNotification: (NSNotification *)note; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /MANotificationCenter.m: -------------------------------------------------------------------------------- 1 | 2 | #import "MANotificationCenter.h" 3 | 4 | 5 | @interface _MANotificationCenterDictionaryKey : NSObject 6 | { 7 | NSString *_name; 8 | id _object; 9 | } 10 | 11 | + (_MANotificationCenterDictionaryKey *)keyForName: (NSString *)name object: (id)obj; 12 | 13 | @end 14 | 15 | @implementation _MANotificationCenterDictionaryKey 16 | 17 | - (id)_initWithName: (NSString *)name object: (id)obj 18 | { 19 | if((self = [self init])) 20 | { 21 | _name = [name copy]; 22 | _object = obj; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)dealloc 28 | { 29 | [_name release]; 30 | [super dealloc]; 31 | } 32 | 33 | + (_MANotificationCenterDictionaryKey *)keyForName: (NSString *)name object: (id)obj 34 | { 35 | return [[[self alloc] _initWithName: name object: obj] autorelease]; 36 | } 37 | 38 | static BOOL Equal(id a, id b) 39 | { 40 | if(!a && !b) 41 | return YES; 42 | else if(!a || !b) 43 | return NO; 44 | else 45 | return [a isEqual: b]; 46 | } 47 | 48 | - (BOOL)isEqual: (id)other 49 | { 50 | if(![other isKindOfClass: [_MANotificationCenterDictionaryKey class]]) 51 | return NO; 52 | 53 | _MANotificationCenterDictionaryKey *otherKey = other; 54 | return Equal(_name, otherKey->_name) && _object == otherKey->_object; 55 | } 56 | 57 | - (NSUInteger)hash 58 | { 59 | return [_name hash] ^ (uintptr_t)_object; 60 | } 61 | 62 | - (id)copyWithZone: (NSZone *)zone 63 | { 64 | return [self retain]; 65 | } 66 | 67 | @end 68 | 69 | @implementation MANotificationCenter 70 | 71 | - (id)init 72 | { 73 | if((self = [super init])) 74 | { 75 | _map = [[NSMutableDictionary alloc] init]; 76 | } 77 | return self; 78 | } 79 | 80 | - (void)dealloc 81 | { 82 | [_map release]; 83 | [super dealloc]; 84 | } 85 | 86 | - (id)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block 87 | { 88 | _MANotificationCenterDictionaryKey *key = [_MANotificationCenterDictionaryKey keyForName: name object: object]; 89 | 90 | NSMutableSet *observerBlocks = [_map objectForKey: key]; 91 | if(!observerBlocks) 92 | { 93 | observerBlocks = [NSMutableSet set]; 94 | [_map setObject: observerBlocks forKey: key]; 95 | } 96 | 97 | void (^copiedBlock)(NSNotification *note); 98 | copiedBlock = [block copy]; 99 | 100 | [observerBlocks addObject: copiedBlock]; 101 | 102 | [copiedBlock release]; 103 | 104 | void (^removalBlock)(void) = ^{ 105 | [observerBlocks removeObject: copiedBlock]; 106 | if([observerBlocks count] == 0) 107 | [_map removeObjectForKey: key]; 108 | }; 109 | 110 | return [[removalBlock copy] autorelease]; 111 | } 112 | 113 | - (void)removeObserver: (id)observer 114 | { 115 | void (^removalBlock)(void) = observer; 116 | removalBlock(); 117 | } 118 | 119 | - (void)_postNotification: (NSNotification *)note name: (NSString *)name object: (id)object 120 | { 121 | _MANotificationCenterDictionaryKey *key = [_MANotificationCenterDictionaryKey keyForName: name object: object]; 122 | NSSet *observerBlocks = [_map objectForKey: key]; 123 | for(void (^block)(NSNotification *) in observerBlocks) 124 | block(note); 125 | } 126 | 127 | - (void)postNotification: (NSNotification *)note 128 | { 129 | NSString *name = [note name]; 130 | id object = [note object]; 131 | 132 | [self _postNotification: note name: name object: object]; 133 | [self _postNotification: note name: name object: nil]; 134 | [self _postNotification: note name: nil object: object]; 135 | [self _postNotification: note name: nil object: nil]; 136 | } 137 | 138 | @end 139 | 140 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | `MANotificationCenter` is an imitation of `NSNotificationCenter` intended for learning purposes. It implements a similar API and shows approximately how `NSNotificationCenter` works on the inside. 2 | 3 | There is no particular reason to use this code for real work, however please feel free to do so if you want to. 4 | 5 | This code was written for this Friday Q&A article: 6 | 7 | http://mikeash.com/pyblog/friday-qa-2011-07-08-lets-build-nsnotificationcenter.html -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // gcc -W -Wall -Wno-unused-parameter -framework Foundation main.m MANotificationCenter.m 2 | 3 | #import "MANotificationCenter.h" 4 | 5 | 6 | int main(int argc, char **argv) 7 | { 8 | [NSAutoreleasePool new]; 9 | 10 | MANotificationCenter *center = [[MANotificationCenter alloc] init]; 11 | 12 | id obj = [[NSObject alloc] init]; 13 | id otherObj = [[NSObject alloc] init]; 14 | 15 | NSMutableArray *removalObjs = [NSMutableArray array]; 16 | id removalObj; 17 | 18 | removalObj = [center addObserverForName: @"name" object: obj block: ^(NSNotification *note) { 19 | NSLog(@"First block got notification %@", note); 20 | }]; 21 | [removalObjs addObject: removalObj]; 22 | 23 | removalObj = [center addObserverForName: @"name" object: obj block: ^(NSNotification *note) { 24 | NSLog(@"Second block got notification %@", note); 25 | }]; 26 | [removalObjs addObject: removalObj]; 27 | 28 | removalObj = [center addObserverForName: @"othername" object: obj block: ^(NSNotification *note) { 29 | NSLog(@"Third block got notification %@", note); 30 | }]; 31 | [removalObjs addObject: removalObj]; 32 | 33 | removalObj = [center addObserverForName: @"name" object: otherObj block: ^(NSNotification *note) { 34 | NSLog(@"Fourth block got notification %@", note); 35 | }]; 36 | [removalObjs addObject: removalObj]; 37 | 38 | removalObj = [center addObserverForName: nil object: obj block: ^(NSNotification *note) { 39 | NSLog(@"Nil name block got notification %@", note); 40 | }]; 41 | [removalObjs addObject: removalObj]; 42 | 43 | removalObj = [center addObserverForName: nil object: otherObj block: ^(NSNotification *note) { 44 | NSLog(@"Second nil name block got notification %@", note); 45 | }]; 46 | [removalObjs addObject: removalObj]; 47 | 48 | removalObj = [center addObserverForName: @"name" object: nil block: ^(NSNotification *note) { 49 | NSLog(@"Nil object block got notification %@", note); 50 | }]; 51 | [removalObjs addObject: removalObj]; 52 | 53 | removalObj = [center addObserverForName: @"othername" object: nil block: ^(NSNotification *note) { 54 | NSLog(@"Second nil object block got notification %@", note); 55 | }]; 56 | [removalObjs addObject: removalObj]; 57 | 58 | removalObj = [center addObserverForName: nil object: nil block: ^(NSNotification *note) { 59 | NSLog(@"Nil-nil catchall block got notification %@", note); 60 | }]; 61 | [removalObjs addObject: removalObj]; 62 | 63 | [center postNotification: [NSNotification notificationWithName: @"name" object: obj]]; 64 | for(id obj in removalObjs) 65 | [center removeObserver: obj]; 66 | [center postNotification: [NSNotification notificationWithName: @"name" object: obj]]; 67 | 68 | return 0; 69 | } 70 | 71 | --------------------------------------------------------------------------------