diff --git a/Headers/gnustep/base/Notification.h b/Headers/gnustep/base/Notification.h new file mode 100644 index 000000000..e6421c3f1 --- /dev/null +++ b/Headers/gnustep/base/Notification.h @@ -0,0 +1,47 @@ +/* Interface for holding and dispatching notifications + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +@interface Notification : NSObject +{ + id _name; + id _object; + id _info; +} + ++ notificationWithName: (id )name + object: object; + ++ notificationWithName: (id )name + object: object + userInfo: info; + +- (id ) name; +- object; +- userInfo; + +@end diff --git a/Headers/gnustep/base/NotificationDispatcher.h b/Headers/gnustep/base/NotificationDispatcher.h new file mode 100644 index 000000000..3674f8036 --- /dev/null +++ b/Headers/gnustep/base/NotificationDispatcher.h @@ -0,0 +1,188 @@ +/* Interface to object for broadcasting Notification objects + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __NotificationDispatcher_h_OBJECTS_INCLUDE +#define __NotificationDispatcher_h_OBJECTS_INCLUDE + +/* A class for posting notifications to observer objects that request + them. + + This implementation has several advantages over OpenStep's + NSNotificationCenter: + + (1) Heavier use of hash tables and the use of LinkedList's make it + faster. Removing from the middle of LinkedList's is much more + efficient than removing from the middle of Array's. + + (2) The way in which notifications are dispatched can be specified as + invocation objects instead of just selectors. Invocation objects + are more flexible than selectors in that they can hold more context + and, if desired, can call C functions instead of sending a message + to an object, (this way you may be able to avoid creating a new + class just to handle certain notifications). + + (3) Instead of sending +defaultCenter, you can simply send -add..., + -remove... and -post... messages directly to the class object. + The class uses a static variable directly, instead of taking + the time for the extra +defaultCenter method call. It's both + easier for the user and more time efficient. + + Although it offers extra features, the implementation has an + OpenStep-style interface also. + + */ + +#include +#include +#include +#include + +@interface NotificationDispatcher : NSObject +{ + /* For those observer requests with NAME=nil and OBJECT=nil. */ + LinkedList *anonymous_nr_list; + /* For those observer requests with NAME=nil and OBJECT!=nil. */ + NSMapTable *object_2_nr_list; + /* For those observer requests with NAME!=nil, OBJECT may or may not =nil .*/ + NSMapTable *name_2_nr_list; + + /* The keys are observers; the values are Array's containing all + NotificationInvocation objects associated with the observer key. */ + NSMapTable *observer_2_nr_array; + + /* `nr' stands for Notification Request Object; the interface for + this class is defined in the .m file. One of these is created + for each -add... call. */ +} + + +/* Adding new observers. */ + +/* Register observer to receive future notifications that match NAME + and OBJECT. A nil passed as either NAME or OBJECT acts as a wild-card. + If NAME is nil, send to the observer all notification pertaining to + OBJECT. If OBJECT is nil, send to the observer all notification + pertaining to NAME. If both OBJECT and NAME are nil, send to the + observer all notifications. + + The notification will be posted by sending -invokeWithObject: to + INVOCATION argument. The argument of -invokeWithObject: will be + a Notification object. This use of Invocation objects is more + flexible than using a selector, since Invocation's can be set up + with more arguments, hold more context, and can be C functions. + + Typically, in cases that INVOCATION is a MethodInvocation, the + target of INVOCATION will the OBSERVER, but this is not required. + When OBSERVER is not the same as the target, and is non-nil, it can + still be useful for organizational help in removing a coherent set + of observation requests, when used as an argument to -removeObserver:. + + Neither OBSERVER nor OBJECT are retained; this is so these objects + can tell when there are no outstanding non-notification references + remaining. If an object may have added itself as an observer, it + should call +removeObserver: in its -dealloc method. + + INVOCATION and NAME, however, are retained. */ + +- (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object; + +/* For those that want the simplicity of specifying a selector instead of + an invocation as a way to contact the observer. + + The notification will be posted by sending -perform:withObject: + to the observer, with SEL and OBJECT as arguments. + + Comments above about retaining apply here also. */ + +- (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object; ++ (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object; + + + +/* Removing observers. */ + +/* Remove all records pertaining to OBSERVER. For instance, this + should be called before the OBSERVER is -dealloc'ed. */ + +- (void) removeObserver: observer; + +/* Remove the notification requests for the given parameters. As with + adding an observation request, nil NAME or OBJECT act as wildcards. */ + +- (void) removeObserver: observer + name: (id )name + object: object; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) removeObserver: observer; ++ (void) removeObserver: observer + name: (id )name + object: object; + + + +/* Post NOTIFICATION to all the observers that match its NAME and OBJECT. */ + +- (void) postNotification: notification; +- (void) postNotificationName: (id )name + object: object; +- (void) postNotificationName: (id )name + object: object + userInfo: (id )info_dictionary; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) postNotification: notification; ++ (void) postNotificationName: (id )name + object: object; ++ (void) postNotificationName: (id )name + object: object + userInfo: (id )info_dictionary; + +@end + +@interface NotificationDispatcher (OpenStepCompat) ++ defaultCenter; +@end + +#endif /* __NotificationDispatcher_h_OBJECTS_INCLUDE */ diff --git a/Source/Notification.m b/Source/Notification.m new file mode 100644 index 000000000..a9fe511a1 --- /dev/null +++ b/Source/Notification.m @@ -0,0 +1,102 @@ +/* Implementation of object for holding a notification + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include + +@implementation Notification + +/* This is the designated initializer. */ +- initWithName: (id )name + object: object + userInfo: info +{ + [super init]; + _name = [name retain]; + _object = [object retain]; + _info = [info retain]; + return self; +} + +- (void) dealloc +{ + [_name release]; + [_object release]; + [_info release]; + [super dealloc]; +} + + +/* Creating autoreleased Notification objects. */ + ++ notificationWithName: (id )name + object: object + userInfo: info +{ + return [[[self alloc] initWithName: name + object: object + userInfo: info] + autorelease]; +} + ++ notificationWithName: (id )name + object: object +{ + return [self notificationWithName: name + object: object + userInfo: nil]; +} + + +/* Querying a Notification object. */ + +- (id ) name +{ + return _name; +} + +- object +{ + return _object; +} + +- userInfo +{ + return _info; +} + + +/* NSCopying protocol. */ + +- copyWithZone: (NSZone*)zone +{ + if (NSShouldRetainWithZone (self, zone)) + return [self retain]; + + /* xxx How deep should the copy go? Should we copy _name, etc.? */ + return [[[self class] allocWithZone: zone] + initWithName: _name + object: _object + userInfo: _info]; +} + +@end diff --git a/Source/NotificationDispatcher.m b/Source/NotificationDispatcher.m new file mode 100644 index 000000000..3a8c94f10 --- /dev/null +++ b/Source/NotificationDispatcher.m @@ -0,0 +1,560 @@ +/* Implementation of object for broadcasting Notification objects + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include + +/* The implementation for NotificationDispatcher. + + First we define an object for holding the observer's + notification requests: */ + + +/* One of these objects is created for each -addObserver... request. + It holds the requested invocation, name and object. Each object + is placed + (1) in one LinkedList, as keyed by the NAME/OBJECT parameters (accessible + through one of the ivars: anonymous_nr_list, object_2_nr_list, + name_2_nr_list), and + (2) in the Array, as keyed by the OBSERVER (as accessible through + the ivar observer_2_nr_array. + + To post a notification in satisfaction of this request, + send -postNotification:. + */ + +@interface NotificationRequest : LinkedListNode +{ + int retain_count; + id name; + id object; +} + +- initWithName: n object: o; +- (id ) notificationName; +- notificationObject; +- (void) postNotification: n; +@end + +@implementation NotificationRequest + +- initWithName: n object: o +{ + [super init]; + retain_count = 0; + name = [n retain]; + object = o; + /* Note that OBJECT is not retained. See the comment for + -addObserver... in NotificationDispatcher.h. */ + return self; +} + +/* Implement these retain/release methods here for efficiency, since + NotificationInvocation's get retained and released by all their + holders. Doing this is a judgement call; I'm choosing speed over + space. */ + +- retain +{ + retain_count++; + return self; +} + +- (oneway void) release +{ + if (!retain_count--) + [self dealloc]; +} + +- (unsigned) retainCount +{ + return retain_count; +} + +- (void) dealloc +{ + [name release]; + [super dealloc]; +} + +- (id ) notificationName +{ + return name; +} + +- notificationObject +{ + return object; +} + +- (void) postNotification: n +{ + [self subclassResponsibility: _cmd]; +} + +@end + + +@interface NotificationInvocation : NotificationRequest +{ + id invocation; +} +- initWithInvocation: i name: n object: o; +@end + +@implementation NotificationInvocation + +- initWithInvocation: i name: n object: o +{ + [super initWithName: n object: o]; + invocation = [i retain]; + return self; +} + +- (void) dealloc +{ + [invocation release]; + [super dealloc]; +} + +- (void) postNotification: n +{ + [invocation invokeWithObject: n]; +} + +@end + + +@interface NotificationPerformer : NotificationRequest +{ + id target; + SEL selector; +} +- initWithTarget: t selector: (SEL)s name: n object: o; +@end + +@implementation NotificationPerformer + +- initWithTarget: t selector: (SEL)s name: n object: o +{ + [super initWithName: n object: o]; + /* Note that TARGET is not retained. See the comment for + -addObserver... in NotificationDispatcher.h. */ + target = t; + selector = s; + return self; +} + +- (void) postNotification: n +{ + [target perform: selector withObject: n]; +} + +@end + + + + +@implementation NotificationDispatcher + +/* The default instance, most often the only one created. + It is accessed by the class methods at the end of this file. */ +static NotificationDispatcher *default_notification_dispatcher = nil; + ++ (void) initialize +{ + if (self == [NotificationDispatcher class]) + default_notification_dispatcher = [self new]; +} + + + +/* Initializing. */ + +- init +{ + [super init]; + anonymous_nr_list = [LinkedList new]; + + /* Use NSNonOwnedPointerOrNullMapKeyCallBacks so we won't retain + the object. We will, however, retain the LinkedList's. */ + object_2_nr_list = + NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + + /* Likewise. */ + /* xxx Should we retain NAME here after all? */ + name_2_nr_list = + NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + + /* Likewise. */ + observer_2_nr_array = + NSCreateMapTable (NSNonOwnedPointerOrNullMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + + return self; +} + + +/* Adding new observers. */ + +/* This is the designated method for adding observers. */ +- (void) _addObserver: observer + notificationRequest: nr + name: (id )name + object: object +{ + /* Record the request in an array of all the requests by this observer. */ + if (observer) + { + Array *nr_array = NSMapGet (observer_2_nr_array, observer); + if (!nr_array) + { + nr_array = [Array new]; + /* nr_array is retained; observer is not. */ + NSMapInsert (observer_2_nr_array, observer, nr_array); + [nr_array release]; + } + [nr_array appendObject: nr]; + } + + /* Record the request in one, and only one, LinkedList. The LinkedList + is stored in a hash table accessed by a key. Which key is used + depends on what combination of NAME and OBJECT are non-nil. */ + if (!name) + { + if (!object) + { + [anonymous_nr_list appendObject: nr]; + } + else + { + LinkedList *nr_list = NSMapGet (object_2_nr_list, object); + if (!nr_list) + { + nr_list = [LinkedList new]; + /* nr_list is retained; object is not retained. */ + NSMapInsert (object_2_nr_list, object, nr_list); + [nr_list release]; + } + [nr_list appendObject: nr]; + } + } + else + { + LinkedList *nr_list = NSMapGet (name_2_nr_list, name); + if (!nr_list) + { + nr_list = [LinkedList new]; + /* nr_list is retained; object is not retained. */ + NSMapInsert (name_2_nr_list, name, nr_list); + [nr_list release]; + } + [nr_list appendObject: nr]; + } + + /* Since nr was retained when it was added to the collection above, + we can release it now. */ + [nr release]; +} + +- (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object +{ + /* The NotificationInvocation we create to hold this request. */ + id nr; + + /* Create the NotificationInvocation object that will hold + this observation request. This will retain INVOCATION and NAME. */ + nr = [[NotificationInvocation alloc] + initWithInvocation: invocation + name: name + object: object]; + + /* Record it in all the right places. */ + [self _addObserver: observer + notificationRequest: nr + name: name + object: object]; +} + + +/* For those that want to specify a selector instead of an invocation + as a way to contact the observer. + If for some reason we didn't want to use Invocation's, we could + additionally create another kind of "NotificationInvocation" that + just used selector's instead. */ + +- (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object +{ + /* The NotificationInvocation we create to hold this request. */ + id nr; + + /* Create the NotificationInvocation object that will hold + this observation request. This will retain INVOCATION and NAME. */ + nr = [[NotificationPerformer alloc] + initWithTarget: observer + selector: sel + name: name + object: object]; + + /* Record it in all the right places. */ + [self _addObserver: observer + notificationRequest: nr + name: name + object: object]; +} + + +/* Remove all records pertaining to OBSERVER. For instance, this + should be called before the OBSERVER is -dealloc'ed. */ + +- (void) removeObserver: observer +{ + Array *observer_nr_array; + NotificationInvocation *nr; + + /* Get the array of NotificationInvocation's associated with OBSERVER. */ + observer_nr_array = NSMapGet (observer_2_nr_array, observer); + + if (!observer_nr_array) + /* OBSERVER was never registered for any notification requests with us. + Nothing to do. */ + return; + + /* Remove each of these from it's LinkedList. */ + FOR_ARRAY (observer_nr_array, nr) + { + [[nr linkedList] removeObject: nr]; + } + END_FOR_ARRAY (observer_nr_array); + + /* Remove from the MapTable the list of NotificationInvocation's + associated with OBSERVER. This also releases the observer_nr_array, + and its contents. */ + NSMapRemove (observer_2_nr_array, observer); +} + + +/* Remove the notification requests for the given parameters. As with + adding an observation request, nil NAME or OBJECT act as wildcards. */ + +- (void) removeObserver: observer + name: (id )name + object: object +{ + Array *observer_nr_array; + NotificationInvocation *nr; + + /* Get the list of NotificationInvocation's associated with OBSERVER. */ + observer_nr_array = NSMapGet (observer_2_nr_array, observer); + + if (!observer_nr_array) + /* OBSERVER was never registered for any notification requests with us. + Nothing to do. */ + return; + + /* Find those NotificationInvocation's from the array that + match NAME and OBJECT, and remove them from the array and + their linked list. */ + { + NotificationInvocation *nr; + int count = [observer_nr_array count]; + unsigned matching_nr_indices[count]; + int i; + + for (i = count-1; i >= 0; i--) + { + nr = [observer_nr_array objectAtIndex: i]; + if ((!name || [name isEqual: [nr notificationName]]) + && (!object || [object isEqual: [nr notificationObject]])) + { + /* We can remove from the array, even though we are "enumerating" + over it, because we are enumerating from back-to-front, + and the indices of yet-to-come objects don't change when + high-indexed objects are removed. */ + [observer_nr_array removeObjectAtIndex: i]; + [[nr linkedList] removeObject: nr]; + } + } + /* xxx If there are some LinkedList's that are empty, I should + remove them from the map table's. */ + } +} + + +/* Post NOTIFICATION to all the observers that match its NAME and OBJECT. */ + +- (void) postNotification: notification +{ + /* This cast avoids complaints about different types for -name. */ + id notification_name = [(Notification*)notification name]; + id notification_object = [notification object]; + id nr; + LinkedList *nr_list; + + /* Make sure the notification has a name. */ + if (!notification_name) + [NSException raise: NSInvalidArgumentException + format: @"Tried to post a notification with no name."]; + + /* Post the notification to all the observers that specified neither + NAME or OBJECT. */ + if ([anonymous_nr_list count]) + { + FOR_COLLECTION (anonymous_nr_list, nr) + { + [nr postNotification: notification]; + } + END_FOR_COLLECTION (anonymous_nr_list); + } + + /* Post the notification to all the observers that specified OBJECT, + but didn't specify NAME. */ + if (notification_object) + { + nr_list = NSMapGet (object_2_nr_list, notification_object); + if (nr_list) + { + FOR_COLLECTION (nr_list, nr) + { + [nr postNotification: notification]; + } + END_FOR_COLLECTION (nr_list); + } + } + + /* Post the notification to all the observers of NAME; (and if the + observer's OBJECT is non-nil, don't send unless the observer's OBJECT + matches the notification's OBJECT). */ + nr_list = NSMapGet (name_2_nr_list, notification_name); + if (nr_list) + { + FOR_COLLECTION (nr_list, nr) + { + id nr_object = [nr notificationObject]; + if (!nr_object || nr_object == notification_object) + [nr postNotification: notification]; + } + END_FOR_COLLECTION (nr_list); + } +} + +- (void) postNotificationName: (id )name + object: object +{ + [self postNotification: [Notification notificationWithName: name + object: object]]; +} + +- (void) postNotificationName: (id )name + object: object + userInfo: info +{ + [self postNotification: [Notification notificationWithName: name + object: object + userInfo: info]]; +} + + + +/* Class methods. */ + ++ (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object +{ + [default_notification_dispatcher addObserver: observer + invocation: invocation + name: name + object: object]; +} + ++ (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object +{ + [default_notification_dispatcher addObserver: observer + selector: sel + name: name + object: object]; +} + ++ (void) removeObserver: observer +{ + [default_notification_dispatcher removeObserver: observer]; +} + ++ (void) removeObserver: observer + name: (id )name + object: object +{ + [default_notification_dispatcher removeObserver: observer + name: name + object: object]; +} + ++ (void) postNotification: notification +{ + [default_notification_dispatcher postNotification: notification]; +} + ++ (void) postNotificationName: (id )name + object: object +{ + [default_notification_dispatcher postNotificationName: name + object: object]; +} + ++ (void) postNotificationName: (id )name + object: object + userInfo: info +{ + [default_notification_dispatcher postNotificationName: name + object: object + userInfo: info]; +} + +@end + +@implementation NotificationDispatcher (OpenStepCompat) + +/* For OpenStep compatibility. */ ++ defaultCenter +{ + return default_notification_dispatcher; +} + +@end diff --git a/Source/objects/Notification.h b/Source/objects/Notification.h new file mode 100644 index 000000000..e6421c3f1 --- /dev/null +++ b/Source/objects/Notification.h @@ -0,0 +1,47 @@ +/* Interface for holding and dispatching notifications + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include + +@interface Notification : NSObject +{ + id _name; + id _object; + id _info; +} + ++ notificationWithName: (id )name + object: object; + ++ notificationWithName: (id )name + object: object + userInfo: info; + +- (id ) name; +- object; +- userInfo; + +@end diff --git a/Source/objects/NotificationDispatcher.h b/Source/objects/NotificationDispatcher.h new file mode 100644 index 000000000..3674f8036 --- /dev/null +++ b/Source/objects/NotificationDispatcher.h @@ -0,0 +1,188 @@ +/* Interface to object for broadcasting Notification objects + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: March 1996 + + This file is part of the GNU Objective C Class Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __NotificationDispatcher_h_OBJECTS_INCLUDE +#define __NotificationDispatcher_h_OBJECTS_INCLUDE + +/* A class for posting notifications to observer objects that request + them. + + This implementation has several advantages over OpenStep's + NSNotificationCenter: + + (1) Heavier use of hash tables and the use of LinkedList's make it + faster. Removing from the middle of LinkedList's is much more + efficient than removing from the middle of Array's. + + (2) The way in which notifications are dispatched can be specified as + invocation objects instead of just selectors. Invocation objects + are more flexible than selectors in that they can hold more context + and, if desired, can call C functions instead of sending a message + to an object, (this way you may be able to avoid creating a new + class just to handle certain notifications). + + (3) Instead of sending +defaultCenter, you can simply send -add..., + -remove... and -post... messages directly to the class object. + The class uses a static variable directly, instead of taking + the time for the extra +defaultCenter method call. It's both + easier for the user and more time efficient. + + Although it offers extra features, the implementation has an + OpenStep-style interface also. + + */ + +#include +#include +#include +#include + +@interface NotificationDispatcher : NSObject +{ + /* For those observer requests with NAME=nil and OBJECT=nil. */ + LinkedList *anonymous_nr_list; + /* For those observer requests with NAME=nil and OBJECT!=nil. */ + NSMapTable *object_2_nr_list; + /* For those observer requests with NAME!=nil, OBJECT may or may not =nil .*/ + NSMapTable *name_2_nr_list; + + /* The keys are observers; the values are Array's containing all + NotificationInvocation objects associated with the observer key. */ + NSMapTable *observer_2_nr_array; + + /* `nr' stands for Notification Request Object; the interface for + this class is defined in the .m file. One of these is created + for each -add... call. */ +} + + +/* Adding new observers. */ + +/* Register observer to receive future notifications that match NAME + and OBJECT. A nil passed as either NAME or OBJECT acts as a wild-card. + If NAME is nil, send to the observer all notification pertaining to + OBJECT. If OBJECT is nil, send to the observer all notification + pertaining to NAME. If both OBJECT and NAME are nil, send to the + observer all notifications. + + The notification will be posted by sending -invokeWithObject: to + INVOCATION argument. The argument of -invokeWithObject: will be + a Notification object. This use of Invocation objects is more + flexible than using a selector, since Invocation's can be set up + with more arguments, hold more context, and can be C functions. + + Typically, in cases that INVOCATION is a MethodInvocation, the + target of INVOCATION will the OBSERVER, but this is not required. + When OBSERVER is not the same as the target, and is non-nil, it can + still be useful for organizational help in removing a coherent set + of observation requests, when used as an argument to -removeObserver:. + + Neither OBSERVER nor OBJECT are retained; this is so these objects + can tell when there are no outstanding non-notification references + remaining. If an object may have added itself as an observer, it + should call +removeObserver: in its -dealloc method. + + INVOCATION and NAME, however, are retained. */ + +- (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object; + +/* For those that want the simplicity of specifying a selector instead of + an invocation as a way to contact the observer. + + The notification will be posted by sending -perform:withObject: + to the observer, with SEL and OBJECT as arguments. + + Comments above about retaining apply here also. */ + +- (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) addObserver: observer + invocation: (id )invocation + name: (id )name + object: object; ++ (void) addObserver: observer + selector: (SEL)sel + name: (id )name + object: object; + + + +/* Removing observers. */ + +/* Remove all records pertaining to OBSERVER. For instance, this + should be called before the OBSERVER is -dealloc'ed. */ + +- (void) removeObserver: observer; + +/* Remove the notification requests for the given parameters. As with + adding an observation request, nil NAME or OBJECT act as wildcards. */ + +- (void) removeObserver: observer + name: (id )name + object: object; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) removeObserver: observer; ++ (void) removeObserver: observer + name: (id )name + object: object; + + + +/* Post NOTIFICATION to all the observers that match its NAME and OBJECT. */ + +- (void) postNotification: notification; +- (void) postNotificationName: (id )name + object: object; +- (void) postNotificationName: (id )name + object: object + userInfo: (id )info_dictionary; + +/* Class versions of the above two methods that send these messages + to the default NotificationDispatcher for the class. */ + ++ (void) postNotification: notification; ++ (void) postNotificationName: (id )name + object: object; ++ (void) postNotificationName: (id )name + object: object + userInfo: (id )info_dictionary; + +@end + +@interface NotificationDispatcher (OpenStepCompat) ++ defaultCenter; +@end + +#endif /* __NotificationDispatcher_h_OBJECTS_INCLUDE */ diff --git a/Testing/nsnotification.m b/Testing/nsnotification.m new file mode 100644 index 000000000..85e620dca --- /dev/null +++ b/Testing/nsnotification.m @@ -0,0 +1,58 @@ +/* The simplest of tests for the NSNotification and NSNotificationCenter + classes. These tests should be expanded. + + (The Tcp*Port classes, however, do test the notification mechanism + further.) */ + +#include +#include + +@interface Observer : NSObject +- (void) gotNotificationFoo: not; +@end + +@implementation Observer + +- (void) gotNotificationFoo: (NSNotification*)not +{ + printf ("Got %@\n", [not name]); +} + +- (void) gotNotificationFooNoObject: (NSNotification*)not +{ + printf ("Got %@ without object\n", [not name]); +} + +@end + +id foo = @"NotificationTestFoo"; + +int main () +{ + id o1 = [NSObject new]; + id observer1 = [Observer new]; + + [[NSNotificationCenter defaultCenter] + addObserver: observer1 + selector: @selector(gotNotificationFoo:) + name: foo + object: o1]; + + [[NSNotificationCenter defaultCenter] + addObserver: observer1 + selector: @selector(gotNotificationFooNoObject:) + name: foo + object: nil]; + + /* This will cause two messages to be printed, one for each request above. */ + [[NSNotificationCenter defaultCenter] + postNotificationName: foo + object: o1]; + + /* This will cause one message to be printed. */ + [[NSNotificationCenter defaultCenter] + postNotificationName: foo + object: nil]; + + exit (0); +}