Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -51,10 +51,12 @@ OFMutableString.m \ OFMutableTarArchiveEntry.m \ OFMutableTriple.m \ OFMutableURL.m \ OFMutableZIPArchiveEntry.m \ + OFNotification.m \ + OFNotificationCenter.m \ OFNull.m \ OFNumber.m \ OFObject.m \ OFObject+KeyValueCoding.m \ OFObject+Serialization.m \ ADDED src/OFNotification.h Index: src/OFNotification.h ================================================================== --- src/OFNotification.h +++ src/OFNotification.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +/** @file */ + +@class OFConstantString; +@class OFDictionary OF_GENERIC(KeyType, ObjectType); + +/** + * @brief A name for a notification. + */ +typedef OFConstantString *OFNotificationName; + +/** + * @class OFNotification OFNotification.h ObjFW/OFNotification.h + * + * @brief A class to represent a notification for or from + * @ref OFNotificationCenter. + */ +OF_SUBCLASSING_RESTRICTED +@interface OFNotification: OFObject +{ + OFNotificationName _name; + id _Nullable _object; + OFDictionary *_Nullable _userInfo; +} + +/** + * @brief The name of the notification. + */ +@property (readonly, nonatomic) OFNotificationName name; + +/** + * @brief The object of the notification. This is commonly the sender of the + * notification. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) id object; + +/** + * @brief Additional information about the notification. + */ +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFDictionary *userInfo; + +/** + * @brief Creates a new notification with the specified name and object. + * + * @param name The name for the notification + * @param object The object for the notification. This is commonly the sender + * of the notification. + * @return A new, autoreleased OFNotification + */ ++ (instancetype)notificationWithName: (OFNotificationName)name + object: (nullable id)object; + +/** + * @brief Creates a new notification with the specified name, object and + * additional information. + * + * @param name The name for the notification + * @param object The object for the notification. This is commonly the sender + * of the notification. + * @param userInfo Additional information for the notification + * @return A new, autoreleased OFNotification + */ ++ (instancetype)notificationWithName: (OFNotificationName)name + object: (nullable id)object + userInfo: (nullable OFDictionary *)userInfo; + +/** + * @brief Initializes an already allocated notification with the specified + * name and object. + * + * @param name The name for the notification + * @param object The object for the notification. This is commonly the sender + * of the notification. + * @return An initialized OFNotification + */ +- (instancetype)initWithName: (OFNotificationName)name + object: (nullable id)object; + +/** + * @brief Initializes an already allocated notification with the specified + * name, object and additional information. + * + * @param name The name for the notification + * @param object The object for the notification. This is commonly the sender + * of the notification. + * @param userInfo Additional information for the notification + * @return An initialized OFNotification + */ +- (instancetype)initWithName: (OFNotificationName)name + object: (nullable id)object + userInfo: (nullable OFDictionary *)userInfo + OF_DESIGNATED_INITIALIZER; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFNotification.m Index: src/OFNotification.m ================================================================== --- src/OFNotification.m +++ src/OFNotification.m @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFNotification.h" +#import "OFDictionary.h" +#import "OFString.h" + +@implementation OFNotification +@synthesize name = _name, object = _object, userInfo = _userInfo; + ++ (instancetype)notificationWithName: (OFNotificationName)name + object: (id)object +{ + return [[[self alloc] initWithName: name object: object] autorelease]; +} + ++ (instancetype)notificationWithName: (OFNotificationName)name + object: (id)object + userInfo: (OFDictionary *)userInfo +{ + return [[[self alloc] initWithName: name + object: object + userInfo: userInfo] autorelease]; +} + +- (instancetype)initWithName: (OFNotificationName)name object: (id)object +{ + return [self initWithName: name object: object userInfo: nil]; +} + +- (instancetype)initWithName: (OFNotificationName)name + object: (id)object + userInfo: (OFDictionary *)userInfo +{ + self = [super init]; + + @try { + _name = [name copy]; + _object = [object retain]; + _userInfo = [userInfo copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_name release]; + [_object release]; + [_userInfo release]; + + [super dealloc]; +} + +- (id)copy +{ + return [self retain]; +} +@end ADDED src/OFNotificationCenter.h Index: src/OFNotificationCenter.h ================================================================== --- src/OFNotificationCenter.h +++ src/OFNotificationCenter.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFObject.h" +#import "OFNotification.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFMutableDictionary OF_GENERIC(KeyType, ObjectType); +#ifdef OF_HAVE_THREADS +@class OFMutex; +#endif +@class OFNotificationCenterHandle; + +#ifdef OF_HAVE_BLOCKS +/** + * @brief A block which is called when a notification has been posted. + * + * @param notification The notification that has been posted + */ +typedef void (^OFNotificationCenterBlock)(OFNotification *notification); +#endif + +/** + * @class OFNotificationCenter OFNotificationCenter.h \ + * ObjFW/OFNotificationCenter.h + * + * @brief A class to send and register for notifications. + */ +@interface OFNotificationCenter: OFObject +{ +#ifdef OF_HAVE_THREADS + OFMutex *_mutex; +#endif + OFMutableDictionary *_handles; + OF_RESERVE_IVARS(OFNotificationCenter, 4) +} + +#ifdef OF_HAVE_CLASS_PROPERTIES +@property (class, readonly, nonatomic) OFNotificationCenter *defaultCenter; +#endif + +/** + * @brief Returns the default notification center. + */ ++ (OFNotificationCenter *)defaultCenter; + +/** + * @brief Adds an observer for the specified notification and object. + * + * @param observer The object that should receive notifications + * @param selector The selector to call on the observer on notifications. The + * method must take exactly one object of type @ref + * OFNotification. + * @param name The name of the notification to observe + * @param object The object that should be sending the notification, or `nil` + * if the object should be ignored to determine what + * notifications to deliver + */ +- (void)addObserver: (id)observer + selector: (SEL)selector + name: (OFNotificationName)name + object: (nullable id)object; + +/** + * @brief Removes an observer. All parameters must match those used with + * @ref addObserver:selector:name:object:. + * + * @param observer The observer that was specified when adding the observer + * @param selector The selector that was specified when adding the observer + * @param name The name that was specified when adding the observer + * @param object The object that was specified when adding the observer + */ +- (void)removeObserver: (id)observer + selector: (SEL)selector + name: (OFNotificationName)name + object: (nullable id)object; + +#ifdef OF_HAVE_BLOCKS +/** + * @brief Adds an observer for the specified notification and object. + * + * To remove the observer again, use @ref removeObserver:. + * + * @param name The name of the notification to observe + * @param object The object that should be sending the notification, or `nil` + * if the object should be ignored to determine what + * notifications to deliver + * @param block The block to handle notifications + * @return An opaque object to remove the observer again + */ +- (OFNotificationCenterHandle *) + addObserverForName: (OFNotificationName)name + object: (nullable id)object + usingBlock: (OFNotificationCenterBlock)block; + +/** + * @brief Removes an observer. The specified observer must be one returned by + * @ref addObserver:selector:name:object:. + * + * @param observer The object that was returned when adding the observer + */ +- (void)removeObserver: (OFNotificationCenterHandle *)observer; +#endif + +/** + * @brief Posts the specified notification. + * + * @param notification The notification to post + */ +- (void)postNotification: (OFNotification *)notification; + +/** + * @brief Posts a notification with the specified name and object. + * + * @param name The name for the notification + * @param object The object for the notification + */ +- (void)postNotificationName: (OFNotificationName)name + object: (nullable id)object; + +/** + * @brief Posts a notification with the specified name, object and additional + * information. + * + * @param name The name for the notification + * @param object The object for the notification + * @param userInfo Additional information for the notification + */ +- (void)postNotificationName: (OFNotificationName)name + object: (nullable id)object + userInfo: (nullable OFDictionary *)userInfo; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFNotificationCenter.m Index: src/OFNotificationCenter.m ================================================================== --- src/OFNotificationCenter.m +++ src/OFNotificationCenter.m @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFNotificationCenter.h" +#import "OFArray.h" +#import "OFDictionary.h" +#ifdef OF_HAVE_THREADS +# import "OFMutex.h" +#endif +#import "OFSet.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" + +@interface OFDefaultNotificationCenter: OFNotificationCenter +@end + +@interface OFNotificationCenterHandle: OFObject +{ +@public + OFNotificationName _name; + id _observer; + SEL _selector; + unsigned long _selectorHash; +#ifdef OF_HAVE_BLOCKS + OFNotificationCenterBlock _block; +#endif + id _object; +} + +- (instancetype)initWithName: (OFNotificationName)name + observer: (id)observer + selector: (SEL)selector + object: (id)object; +#ifdef OF_HAVE_BLOCKS +- (instancetype)initWithName: (OFNotificationName)name + object: (id)object + block: (OFNotificationCenterBlock)block; +#endif +@end + +static OFNotificationCenter *defaultCenter; + +@implementation OFNotificationCenterHandle +- (instancetype)initWithName: (OFNotificationName)name + observer: (id)observer + selector: (SEL)selector + object: (id)object +{ + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + + _name = [name copy]; + _observer = [observer retain]; + _selector = selector; + _object = [object retain]; + + _selectorHash = [[OFString stringWithUTF8String: + sel_getName(_selector)] hash]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +#ifdef OF_HAVE_BLOCKS +- (instancetype)initWithName: (OFNotificationName)name + object: (id)object + block: (OFNotificationCenterBlock)block +{ + self = [super init]; + + @try { + _name = [name copy]; + _object = [object retain]; + _block = [block copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +#endif + +- (void)dealloc +{ + [_name release]; + [_observer release]; + [_object release]; +#ifdef OF_HAVE_BLOCKS + [_block release]; +#endif + + [super dealloc]; +} + +- (bool)isEqual: (OFNotificationCenterHandle *)handle +{ + if (![handle isKindOfClass: [OFNotificationCenterHandle class]]) + return false; + + if (![handle->_name isEqual: _name]) + return false; + + if (handle->_observer != _observer && + ![handle->_observer isEqual: _observer]) + return false; + + if (handle->_selector != _selector && + !sel_isEqual(handle->_selector, _selector)) + return false; + +#ifdef OF_HAVE_BLOCKS + if (handle->_block != _block) + return false; +#endif + + if (handle->_object != _object && ![handle->_object isEqual: _object]) + return false; + + return true; +} + +- (unsigned long)hash +{ + unsigned long hash; + + OFHashInit(&hash); + + OFHashAddHash(&hash, _name.hash); + OFHashAddHash(&hash, [_observer hash]); + OFHashAddHash(&hash, _selectorHash); +#ifdef OF_HAVE_BLOCKS + if (_block != NULL) + OFHashAddHash(&hash, (unsigned long)(uintptr_t)_block); +#endif + OFHashAddHash(&hash, [_object hash]); + + OFHashFinalize(&hash); + + return hash; +} +@end + +@implementation OFNotificationCenter ++ (void)initialize +{ + if (self != [OFNotificationCenter class]) + return; + + defaultCenter = [[OFDefaultNotificationCenter alloc] init]; +} + ++ (OFNotificationCenter *)defaultCenter +{ + return defaultCenter; +} + +- (instancetype)init +{ + self = [super init]; + + @try { +#ifdef OF_HAVE_THREADS + _mutex = [[OFMutex alloc] init]; +#endif + _handles = [[OFMutableDictionary alloc] init]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ +#ifdef OF_HAVE_THREADS + [_mutex release]; +#endif + [_handles release]; + + [super dealloc]; +} + +- (void)of_addObserver: (OFNotificationCenterHandle *)handle +{ +#ifdef OF_HAVE_THREADS + [_mutex lock]; + @try { +#endif + OFMutableSet *handlesForName = + [_handles objectForKey: handle->_name]; + + if (handlesForName == nil) { + handlesForName = [OFMutableSet set]; + [_handles setObject: handlesForName + forKey: handle->_name]; + } + + [handlesForName addObject: handle]; +#ifdef OF_HAVE_THREADS + } @finally { + [_mutex unlock]; + } +#endif +} + +- (void)addObserver: (id)observer + selector: (SEL)selector + name: (OFNotificationName)name + object: (id)object +{ + void *pool = objc_autoreleasePoolPush(); + + [self of_addObserver: + [[[OFNotificationCenterHandle alloc] initWithName: name + observer: observer + selector: selector + object: object] + autorelease]]; + + objc_autoreleasePoolPop(pool); +} + +#ifdef OF_HAVE_BLOCKS +- (OFNotificationCenterHandle *) + addObserverForName: (OFNotificationName)name + object: (id)object + usingBlock: (OFNotificationCenterBlock)block +{ + void *pool = objc_autoreleasePoolPush(); + OFNotificationCenterHandle *handle = + [[[OFNotificationCenterHandle alloc] initWithName: name + object: object + block: block] + autorelease]; + + [self of_addObserver: handle]; + + [handle retain]; + + objc_autoreleasePoolPop(pool); + + return [handle autorelease]; +} +#endif + +- (void)removeObserver: (OFNotificationCenterHandle *)handle +{ + void *pool = objc_autoreleasePoolPush(); + + /* {} required to avoid -Wmisleading-indentation false positive. */ + if (![handle isKindOfClass: [OFNotificationCenterHandle class]]) { + @throw [OFInvalidArgumentException exception]; + } + +#ifdef OF_HAVE_THREADS + [_mutex lock]; + @try { +#endif + OFNotificationName name = [[handle->_name copy] autorelease]; + OFMutableSet *handlesForName = [_handles objectForKey: name]; + + [handlesForName removeObject: handle]; + + if (handlesForName.count == 0) + [_handles removeObjectForKey: name]; +#ifdef OF_HAVE_THREADS + } @finally { + [_mutex unlock]; + } +#endif + + objc_autoreleasePoolPop(pool); +} + +- (void)removeObserver: (id)observer + selector: (SEL)selector + name: (OFNotificationName)name + object: (id)object +{ + void *pool = objc_autoreleasePoolPush(); + + [self removeObserver: + [[[OFNotificationCenterHandle alloc] initWithName: name + observer: observer + selector: selector + object: object] + autorelease]]; + + objc_autoreleasePoolPop(pool); +} + +- (void)postNotification: (OFNotification *)notification +{ + void *pool = objc_autoreleasePoolPush(); + OFMutableArray *matchedHandles = [OFMutableArray array]; + +#ifdef OF_HAVE_THREADS + [_mutex lock]; + @try { +#endif + for (OFNotificationCenterHandle *handle in + [_handles objectForKey: notification.name]) + if (handle->_object == nil || + handle->_object == notification.object) + [matchedHandles addObject: handle]; +#ifdef OF_HAVE_THREADS + } @finally { + [_mutex unlock]; + } +#endif + + for (OFNotificationCenterHandle *handle in matchedHandles) { +#ifdef OF_HAVE_BLOCKS + if (handle->_block != NULL) + handle->_block(notification); + else { +#endif + void (*callback)(id, SEL, OFNotification *) = + (void (*)(id, SEL, OFNotification *)) + [handle->_observer methodForSelector: + handle->_selector]; + + callback(handle->_observer, handle->_selector, + notification); +#ifdef OF_HAVE_BLOCKS + } +#endif + } + + objc_autoreleasePoolPop(pool); +} + +- (void)postNotificationName: (OFNotificationName)name + object: (nullable id)object +{ + [self postNotificationName: name object: object userInfo: nil]; +} + +- (void)postNotificationName: (OFNotificationName)name + object: (nullable id)object + userInfo: (nullable OFDictionary *)userInfo +{ + void *pool = objc_autoreleasePoolPush(); + + [self postNotification: + [OFNotification notificationWithName: name + object: object + userInfo: userInfo]]; + + objc_autoreleasePoolPop(pool); +} +@end + +@implementation OFDefaultNotificationCenter +- (instancetype)autorelease +{ + return self; +} + +- (instancetype)retain +{ + return self; +} + +- (void)release +{ +} + +- (unsigned int)retainCount +{ + return OFMaxRetainCount; +} +@end Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -1414,17 +1414,14 @@ - (void)of_removeTimer: (OFTimer *)timer forMode: (OFRunLoopMode)mode { OFRunLoopState *state = stateForMode(self, mode, false); - if (state == nil) + /* {} required to avoid -Wmisleading-indentation false positive. */ + if (state == nil) { return; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wmisleading-indentation" + } #ifdef OF_HAVE_THREADS [state->_timersQueueMutex lock]; @try { #endif @@ -1438,12 +1435,10 @@ #ifdef OF_HAVE_THREADS } @finally { [state->_timersQueueMutex unlock]; } #endif - -#pragma GCC diagnostic pop } #ifdef OF_AMIGAOS - (void)addExecSignal: (ULONG)signal target: (id)target selector: (SEL)selector { Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -48,10 +48,13 @@ #import "OFUUID.h" #import "OFURL.h" #import "OFURLHandler.h" #import "OFColor.h" +#import "OFNotification.h" +#import "OFNotificationCenter.h" + #import "OFStream.h" #import "OFStdIOStream.h" #import "OFInflateStream.h" #import "OFInflate64Stream.h" #import "OFGZIPStream.h" Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -22,10 +22,11 @@ OFInvocationTests.m \ OFJSONTests.m \ OFListTests.m \ OFLocaleTests.m \ OFMethodSignatureTests.m \ + OFNotificationCenterTests.m \ OFNumberTests.m \ OFObjectTests.m \ OFPBKDF2Tests.m \ OFPropertyListTests.m \ OFScryptTests.m \ ADDED tests/OFNotificationCenterTests.m Index: tests/OFNotificationCenterTests.m ================================================================== --- tests/OFNotificationCenterTests.m +++ tests/OFNotificationCenterTests.m @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2008-2021 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "TestsAppDelegate.h" + +static OFString *const module = @"OFNotificationCenter"; +static const OFNotificationName notificationName = + @"OFNotificationCenterTestName"; +static const OFNotificationName otherNotificationName = + @"OFNotificationCenterTestOtherName"; + +@interface OFNotificationCenterTest: OFObject +{ +@public + id _expectedObject; + int _received; +} + +- (void)handleNotification: (OFNotification *)notification; +@end + +@implementation OFNotificationCenterTest +- (void)handleNotification: (OFNotification *)notification +{ + OFEnsure([notification.name isEqual: notificationName]); + OFEnsure(_expectedObject == nil || + notification.object == _expectedObject); + + _received++; +} +@end + +@implementation TestsAppDelegate (OFNotificationCenterTests) +- (void)notificationCenterTests +{ + void *pool = objc_autoreleasePoolPush(); + OFNotificationCenter *center = [OFNotificationCenter defaultCenter]; + OFNotificationCenterTest *test1, *test2, *test3, *test4; + OFNotification *notification; + + test1 = + [[[OFNotificationCenterTest alloc] init] autorelease]; + test1->_expectedObject = self; + test2 = + [[[OFNotificationCenterTest alloc] init] autorelease]; + test3 = + [[[OFNotificationCenterTest alloc] init] autorelease]; + test3->_expectedObject = self; + test4 = + [[[OFNotificationCenterTest alloc] init] autorelease]; + + /* First one intentionally added twice to test deduplication. */ + TEST(@"-[addObserver:selector:name:object:]", + R([center addObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]) && + R([center addObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]) && + R([center addObserver: test2 + selector: @selector(handleNotification:) + name: notificationName + object: nil]) && + R([center addObserver: test3 + selector: @selector(handleNotification:) + name: otherNotificationName + object: self]) && + R([center addObserver: test4 + selector: @selector(handleNotification:) + name: otherNotificationName + object: nil])) + + notification = [OFNotification notificationWithName: notificationName + object: nil]; + TEST(@"-[postNotification:] #1", + R([center postNotification: notification]) && + test1->_received == 0 && test2->_received == 1 && + test3->_received == 0 && test4->_received == 0) + + notification = [OFNotification notificationWithName: notificationName + object: self]; + TEST(@"-[postNotification:] #2", + R([center postNotification: notification]) && + test1->_received == 1 && test2->_received == 2 && + test3->_received == 0 && test4->_received == 0) + + notification = [OFNotification notificationWithName: notificationName + object: @"foo"]; + TEST(@"-[postNotification:] #3", + R([center postNotification: notification]) && + test1->_received == 1 && test2->_received == 3 && + test3->_received == 0 && test4->_received == 0) + +#ifdef OF_HAVE_BLOCKS + __block bool received = false; + OFNotificationCenterHandle *handle; + + notification = [OFNotification notificationWithName: notificationName + object: self]; + TEST(@"-[addObserverForName:object:usingBlock:]", + (handle = [center addObserverForName: notificationName + object: self + usingBlock: ^ (OFNotification *notif) { + OFEnsure(notif == notification && !received); + received = true; + }]) && R([center postNotification: notification]) && received && + test1->_received == 2 && test2->_received == 4 && + test3->_received == 0 && test4->_received == 0) + + /* Act like the block test didn't happen. */ + [center removeObserver: handle]; + test1->_received--; + test2->_received--; +#endif + + TEST(@"-[removeObserver:selector:name:object:]", + R([center removeObserver: test1 + selector: @selector(handleNotification:) + name: notificationName + object: self]) && + R([center removeObserver: test2 + selector: @selector(handleNotification:) + name: notificationName + object: nil]) && + R([center removeObserver: test3 + selector: @selector(handleNotification:) + name: otherNotificationName + object: self]) && + R([center removeObserver: test4 + selector: @selector(handleNotification:) + name: otherNotificationName + object: nil])) + + notification = [OFNotification notificationWithName: notificationName + object: self]; + TEST(@"-[postNotification:] with no observers", + R([center postNotification: notification]) && + test1->_received == 1 && test2->_received == 3 && + test3->_received == 0 && test4->_received == 0) + + objc_autoreleasePoolPop(pool); +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -136,10 +136,14 @@ @end @interface TestsAppDelegate (OFMethodSignatureTests) - (void)methodSignatureTests; @end + +@interface TestsAppDelegate (OFNotificationCenterTests) +- (void)notificationCenterTests; +@end @interface TestsAppDelegate (OFNumberTests) - (void)numberTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -329,10 +329,11 @@ [self setTests]; [self dateTests]; [self valueTests]; [self numberTests]; [self streamTests]; + [self notificationCenterTests]; #ifdef OF_HAVE_FILES [self MD5HashTests]; [self RIPEMD160HashTests]; [self SHA1HashTests]; [self SHA224HashTests];