/* * 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" @interface OFDefaultNotificationCenter: OFNotificationCenter @end @interface OFNotificationHandler: OFObject { @public id _observer; SEL _selector; unsigned long _selectorHash; id _object; } - (instancetype)initWithObserver: (id)observer selector: (SEL)selector object: (id)object; @end static OFNotificationCenter *defaultCenter; @implementation OFNotificationHandler - (instancetype)initWithObserver: (id)observer selector: (SEL)selector object: (id)object { self = [super init]; @try { void *pool = objc_autoreleasePoolPush(); _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; } - (void)dealloc { [_observer release]; [_object release]; [super dealloc]; } - (bool)isEqual: (OFNotificationHandler *)handler { if (![handler isKindOfClass: [OFNotificationHandler class]]) return false; if (![handler->_observer isEqual: _observer]) return false; if (!sel_isEqual(handler->_selector, _selector)) return false; if (handler->_object != _object && ![handler->_object isEqual: _object]) return false; return true; } - (unsigned long)hash { unsigned long hash; OFHashInit(&hash); OFHashAddHash(&hash, [_observer hash]); OFHashAddHash(&hash, _selectorHash); 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 _notifications = [[OFMutableDictionary alloc] init]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { #ifdef OF_HAVE_THREADS [_mutex release]; #endif [_notifications release]; [super dealloc]; } - (void)addObserver: (id)observer selector: (SEL)selector name: (OFNotificationName)name object: (id)object { void *pool = objc_autoreleasePoolPush(); OFNotificationHandler *handler = [[[OFNotificationHandler alloc] initWithObserver: observer selector: selector object: object] autorelease]; #ifdef OF_HAVE_THREADS [_mutex lock]; @try { #endif OFMutableSet *notificationsForName = [_notifications objectForKey: name]; if (notificationsForName == nil) { notificationsForName = [OFMutableSet set]; [_notifications setObject: notificationsForName forKey: name]; } [notificationsForName addObject: handler]; #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(); OFNotificationHandler *handler = [[[OFNotificationHandler alloc] initWithObserver: observer selector: selector object: object] autorelease]; #ifdef OF_HAVE_THREADS [_mutex lock]; @try { #endif [[_notifications objectForKey: name] removeObject: handler]; #ifdef OF_HAVE_THREADS } @finally { [_mutex unlock]; } #endif objc_autoreleasePoolPop(pool); } - (void)postNotification: (OFNotification *)notification { void *pool = objc_autoreleasePoolPush(); OFMutableArray *matchedHandlers = [OFMutableArray array]; #ifdef OF_HAVE_THREADS [_mutex lock]; @try { #endif for (OFNotificationHandler *handler in [_notifications objectForKey: notification.name]) if (handler->_object == nil || handler->_object == notification.object) [matchedHandlers addObject: handler]; #ifdef OF_HAVE_THREADS } @finally { [_mutex unlock]; } #endif for (OFNotificationHandler *handler in matchedHandlers) { void (*callback)(id, SEL, OFNotification *) = (void (*)(id, SEL, OFNotification *)) [handler->_observer methodForSelector: handler->_selector]; callback(handler->_observer, handler->_selector, notification); } objc_autoreleasePoolPop(pool); } @end @implementation OFDefaultNotificationCenter - (instancetype)autorelease { return self; } - (instancetype)retain { return self; } - (void)release { } - (unsigned int)retainCount { return OFMaxRetainCount; } @end