New file.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1054 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Andrew McCallum 1996-03-03 02:18:46 +00:00
parent c86c5f3459
commit dba54a8bd3
7 changed files with 1190 additions and 0 deletions

View file

@ -0,0 +1,47 @@
/* Interface for holding and dispatching notifications
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/stdobjects.h>
#include <objects/NSString.h>
#include <objects/KeyedCollecting.h>
#include <objects/NotificationDispatcher.h>
@interface Notification : NSObject
{
id _name;
id _object;
id _info;
}
+ notificationWithName: (id <String>)name
object: object;
+ notificationWithName: (id <String>)name
object: object
userInfo: info;
- (id <String>) name;
- object;
- userInfo;
@end

View file

@ -0,0 +1,188 @@
/* Interface to object for broadcasting Notification objects
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/stdobjects.h>
#include <objects/LinkedList.h>
#include <Foundation/NSMapTable.h>
#include <objects/NSString.h>
@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 <Invoking>)invocation
name: (id <String>)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 <String>)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 <Invoking>)invocation
name: (id <String>)name
object: object;
+ (void) addObserver: observer
selector: (SEL)sel
name: (id <String>)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 <String>)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 <String>)name
object: object;
/* Post NOTIFICATION to all the observers that match its NAME and OBJECT. */
- (void) postNotification: notification;
- (void) postNotificationName: (id <String>)name
object: object;
- (void) postNotificationName: (id <String>)name
object: object
userInfo: (id <ConstantKeyedCollecting>)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 <String>)name
object: object;
+ (void) postNotificationName: (id <String>)name
object: object
userInfo: (id <ConstantKeyedCollecting>)info_dictionary;
@end
@interface NotificationDispatcher (OpenStepCompat)
+ defaultCenter;
@end
#endif /* __NotificationDispatcher_h_OBJECTS_INCLUDE */

102
Source/Notification.m Normal file
View file

@ -0,0 +1,102 @@
/* Implementation of object for holding a notification
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/Notification.h>
@implementation Notification
/* This is the designated initializer. */
- initWithName: (id <String>)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 <String>)name
object: object
userInfo: info
{
return [[[self alloc] initWithName: name
object: object
userInfo: info]
autorelease];
}
+ notificationWithName: (id <String>)name
object: object
{
return [self notificationWithName: name
object: object
userInfo: nil];
}
/* Querying a Notification object. */
- (id <String>) 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

View file

@ -0,0 +1,560 @@
/* Implementation of object for broadcasting Notification objects
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/NotificationDispatcher.h>
#include <objects/Notification.h>
#include <objects/LinkedListNode.h>
#include <objects/Array.h>
#include <objects/Invocation.h>
#include <Foundation/NSException.h>
/* 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 <String>) 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 <String>) 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 <String>)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 <Invoking>)invocation
name: (id <String>)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 <String>)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 <String>)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 <String>)name
object: object
{
[self postNotification: [Notification notificationWithName: name
object: object]];
}
- (void) postNotificationName: (id <String>)name
object: object
userInfo: info
{
[self postNotification: [Notification notificationWithName: name
object: object
userInfo: info]];
}
/* Class methods. */
+ (void) addObserver: observer
invocation: (id <Invoking>)invocation
name: (id <String>)name
object: object
{
[default_notification_dispatcher addObserver: observer
invocation: invocation
name: name
object: object];
}
+ (void) addObserver: observer
selector: (SEL)sel
name: (id <String>)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 <String>)name
object: object
{
[default_notification_dispatcher removeObserver: observer
name: name
object: object];
}
+ (void) postNotification: notification
{
[default_notification_dispatcher postNotification: notification];
}
+ (void) postNotificationName: (id <String>)name
object: object
{
[default_notification_dispatcher postNotificationName: name
object: object];
}
+ (void) postNotificationName: (id <String>)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

View file

@ -0,0 +1,47 @@
/* Interface for holding and dispatching notifications
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/stdobjects.h>
#include <objects/NSString.h>
#include <objects/KeyedCollecting.h>
#include <objects/NotificationDispatcher.h>
@interface Notification : NSObject
{
id _name;
id _object;
id _info;
}
+ notificationWithName: (id <String>)name
object: object;
+ notificationWithName: (id <String>)name
object: object
userInfo: info;
- (id <String>) name;
- object;
- userInfo;
@end

View file

@ -0,0 +1,188 @@
/* Interface to object for broadcasting Notification objects
Copyright (C) 1996 Free Software Foundation, Inc.
Written by: R. Andrew McCallum <mccallum@gnu.ai.mit.edu>
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 <objects/stdobjects.h>
#include <objects/LinkedList.h>
#include <Foundation/NSMapTable.h>
#include <objects/NSString.h>
@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 <Invoking>)invocation
name: (id <String>)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 <String>)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 <Invoking>)invocation
name: (id <String>)name
object: object;
+ (void) addObserver: observer
selector: (SEL)sel
name: (id <String>)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 <String>)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 <String>)name
object: object;
/* Post NOTIFICATION to all the observers that match its NAME and OBJECT. */
- (void) postNotification: notification;
- (void) postNotificationName: (id <String>)name
object: object;
- (void) postNotificationName: (id <String>)name
object: object
userInfo: (id <ConstantKeyedCollecting>)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 <String>)name
object: object;
+ (void) postNotificationName: (id <String>)name
object: object
userInfo: (id <ConstantKeyedCollecting>)info_dictionary;
@end
@interface NotificationDispatcher (OpenStepCompat)
+ defaultCenter;
@end
#endif /* __NotificationDispatcher_h_OBJECTS_INCLUDE */

58
Testing/nsnotification.m Normal file
View file

@ -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 <Foundation/NSNotification.h>
#include <Foundation/NSString.h>
@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);
}