diff --git a/Foundation/CPNotificationCenter.j b/Foundation/CPNotificationCenter.j index 99ab181837..0d880276ed 100644 --- a/Foundation/CPNotificationCenter.j +++ b/Foundation/CPNotificationCenter.j @@ -68,7 +68,8 @@ var CPNotificationDefaultCenter = nil; _namedRegistries = @{}; _unnamedRegistry = [[_CPNotificationRegistry alloc] init]; } - return self; + + return self; } /*! @@ -81,9 +82,35 @@ var CPNotificationDefaultCenter = nil; */ - (void)addObserver:(id)anObserver selector:(SEL)aSelector name:(CPString)aNotificationName object:(id)anObject { - var registry, + var registry = [self _registryForNotificationName:aNotificationName], observer = [[_CPNotificationObserver alloc] initWithObserver:anObserver selector:aSelector]; + [registry addObserver:observer object:anObject]; +} + +/*! + Adds an entry to the receiver’s dispatch table with a block, and optional criteria: notification name and sender. + @param aNotificationName the name of the notification the observer wants to watch + @param anObject the object in the notification the observer wants to watch + @param block the block to be executed when the notification is received. +*/ +- (id )addObserverForName:(CPString)aNotificationName object:(id)anObject usingBlock:(Function)block +{ + var registry = [self _registryForNotificationName:aNotificationName], + observer = [[_CPNotificationObserver alloc] initWithBlock:block]; + + [registry addObserver:observer object:anObject]; + + return observer; +} + +/*! + @ignore +*/ +- (_CPNotificationRegistry)_registryForNotificationName:(CPString)aNotificationName +{ + var registry; + if (aNotificationName == nil) registry = _unnamedRegistry; else if (!(registry = [_namedRegistries objectForKey:aNotificationName])) @@ -92,7 +119,7 @@ var CPNotificationDefaultCenter = nil; [_namedRegistries setObject:registry forKey:aNotificationName]; } - [registry addObserver:observer object:anObject]; + return registry; } /*! @@ -129,7 +156,10 @@ var CPNotificationDefaultCenter = nil; [_unnamedRegistry removeObserver:anObserver object:anObject]; } else + { [[_namedRegistries objectForKey:aNotificationName] removeObserver:anObserver object:anObject]; + } + } /*! @@ -233,7 +263,8 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ observersEnumerator = [observers objectEnumerator]; while ((observer = [observersEnumerator nextObject]) !== nil) - if ([observer observer] == anObserver) + if ([observer observer] == anObserver || + ([observer block] && [anObserver respondsToSelector:@selector(block)] && [observer block] == [anObserver block])) [observers removeObject:observer]; if (![observers count]) @@ -248,7 +279,8 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ observersEnumerator = [observers objectEnumerator]; while ((observer = [observersEnumerator nextObject]) !== nil) - if ([observer observer] == anObserver) + if ([observer observer] == anObserver || + ([observer block] && [anObserver respondsToSelector:@selector(block)] && [observer block] == [anObserver block])) [observers removeObject:observer]; if (![observers count]) @@ -312,8 +344,9 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ /* @ignore */ @implementation _CPNotificationObserver : CPObject { - id _observer; - SEL _selector; + id _observer; + Function _block; + SEL _selector; } - (id)initWithObserver:(id)anObserver selector:(SEL)aSelector @@ -327,13 +360,34 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ return self; } +- (id)initWithBlock:(Function)aBlock +{ + if (self) + { + _block = aBlock; + } + + return self; +} + - (id)observer { return _observer; } +- (id)block +{ + return _block; +} + - (void)postNotification:(CPNotification)aNotification { + if (_block) + { + _block(aNotification); + return; + } + [_observer performSelector:_selector withObject:aNotification]; } diff --git a/Tests/Foundation/CPNotificationCenterTest.j b/Tests/Foundation/CPNotificationCenterTest.j index b1f3b8bf58..e630e4064c 100644 --- a/Tests/Foundation/CPNotificationCenterTest.j +++ b/Tests/Foundation/CPNotificationCenterTest.j @@ -52,6 +52,51 @@ var TestNotification = @"TestNotification"; [self assert:8 equals:notificationCount message:@"observer should not be notified"]; } +- (void)testNotifyWithBlocks +{ + var center = [CPNotificationCenter defaultCenter]; + + notificationCount = 0; + + var notificationBlock = function(notification){ + notificationCount += 1; + }; + + var observer2 = [center addObserverForName:TestNotification object:2 usingBlock:notificationBlock], + observer25 = [center addObserverForName:TestNotification object:25 usingBlock:notificationBlock]; + + [center postNotificationName:TestNotification object:self]; + [self assert:0 equals:notificationCount message:@"observer should only be notified for object '2'"]; + + [center postNotificationName:TestNotification object:2]; + [self assert:1 equals:notificationCount message:@"observer should be notified for object '2'"]; + + var observerNil = [center addObserverForName:TestNotification object:nil usingBlock:notificationBlock]; + + [center postNotificationName:TestNotification object:2]; + [self assert:3 equals:notificationCount message:@"observer should be notified for object '2' and for any object"]; + + [center removeObserver:observer2 name:TestNotification object:2]; + [center postNotificationName:TestNotification object:2]; + [self assert:4 equals:notificationCount message:@"observer should be notified only for any object"]; + + // At this point we have TestNofication:nil observer and a TestNotification:25 observer. + observer2 = [center addObserverForName:nil object:2 usingBlock:notificationBlock]; + + [center postNotificationName:TestNotification object:2]; + [self assert:6 equals:notificationCount message:@"observer should be notified for TestNofication and for object '2' (TestNotification)"]; + [center postNotificationName:@"RandomNotification" object:2]; + [self assert:7 equals:notificationCount message:@"observer should be notified for object '2' (RandomNotification)"]; + + [center removeObserver:observer2]; + [center removeObserver:observer25]; + [center removeObserver:observerNil]; + [center postNotificationName:TestNotification object:nil]; + [center postNotificationName:TestNotification object:2]; + [center postNotificationName:TestNotification object:25]; + [self assert:7 equals:notificationCount message:@"observer should not be notified"]; +} + - (void)testAddObserversDuringNotification { var center = [CPNotificationCenter defaultCenter];