Rewrite notification center.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@4419 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 1999-06-17 13:17:28 +00:00
parent 53339afb05
commit 98c8bc80d8
4 changed files with 944 additions and 80 deletions

View file

@ -1,3 +1,10 @@
Thu Jun 17 14:30:00 1999 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* Source/include/NSNotification.h: Update to MacOS-X specs.
* Source/NSNotification.m: Tidied
* Source/NSNotificationCenter.m: Complete rewrite for performance
(order of magnitude improvement) and GC.
Thu Jun 17 12:00:00 1999 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* Tools/gdnc.m: Only use OpenStep NSNotificationCenter methods.

View file

@ -1,5 +1,5 @@
/* Interface for NSNotification and NSNotificationCenter for GNUStep
Copyright (C) 1996 Free Software Foundation, Inc.
/* Interface for NSNotification and NSNotificationCenter for GNUstep
Copyright (C) 1996,1999 Free Software Foundation, Inc.
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Created: March 1996
@ -25,85 +25,65 @@
#define __NSNotification_h_GNUSTEP_BASE_INCLUDE
#include <Foundation/NSObject.h>
#include <Foundation/NSMapTable.h>
@class NSString;
@class NSDictionary;
@class NSLock;
@protocol Notifying
- (NSString*) name;
- object;
- userInfo;
@end
@protocol NotificationPosting
- (void) postNotification: (id <Notifying>)notification;
@end
@interface NSNotification : NSObject <Notifying,NSCopying>
@interface NSNotification : NSObject <NSCopying, NSCoding>
{
id _name;
id _object;
id _info;
NSString *_name;
id _object;
NSDictionary *_info;
}
/* Creating a Notification Object */
+ (NSNotification*) notificationWithName: (NSString*)name
object: object;
object: (id)object;
+ (NSNotification*) notificationWithName: (NSString*)name
object: object
userInfo: (NSDictionary*)user_info;
object: (id)object
userInfo: (NSDictionary*)user_info;
/* Querying a Notification Object */
- (NSString*) name;
- object;
- (id) object;
- (NSDictionary*) userInfo;
@end
#include <base/NotificationDispatcher.h>
/* Put this in a category to avoid unimportant errors due to behaviors. */
@interface NSNotificationCenter : NSObject
/* Make the instance size of this class match exactly the instance
size of NotificationDispatcher. Thus, behavior_class_add_class() will not
have to increase the instance size of NSNotificationCenter, and
NSNotificationCenter can safely be subclassed. */
char _NSNotificationCenter_placeholder[(sizeof(struct NotificationDispatcher)
- sizeof(struct NSObject))];
@end
#ifndef NO_GNUSTEP
@interface NSNotificationCenter (GNUstep)
/* Getting the default NotificationCenter */
@interface NSNotificationCenter : NSObject <GCFinalization>
{
void *wildcard; /* Observations matching anything. */
NSMapTable *nameless; /* Observations matching objects. */
void *named; /* Observations matching names. */
NSMapTable *observers; /* Observations keyed by observer. */
NSLock *_lock;
}
+ (NSNotificationCenter*) defaultCenter;
/* Adding and removing observers */
- (void) addObserver: anObserver
selector: (SEL)selector
- (void) addObserver: (id)observer
selector: (SEL)sel
name: (NSString*)name
object: object;
object: (id)object;
- (void) removeObserver: anObserver;
- (void) removeObserver: anObserver
name: (NSString*)name
object: object;
- (void) removeObserver: (id)observer;
- (void) removeObserver: (id)observer
name: (NSString*)name
object: (id)object;
/* Posting Notifications */
- (void) postNotification: (NSNotification*)aNotification;
- (void) postNotification: (NSNotification*)notification;
- (void) postNotificationName: (NSString*)name
object: object;
object: (id)object;
- (void) postNotificationName: (NSString*)name
object: object
userInfo: (NSDictionary*)user_info;
object: (id)object
userInfo: (NSDictionary*)info;
@end
#endif
#endif /*__NSNotification_h_GNUSTEP_BASE_INCLUDE */

View file

@ -23,45 +23,44 @@
#include <config.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSString.h>
@implementation NSNotification
/* This is the designated initializer. */
- initWithName: (NSString*)name
object: object
userInfo: info
- (id) initWithName: (NSString*)name
object: (id)object
userInfo: (id)info
{
[super init];
_name = [name copyWithZone: NSDefaultMallocZone()];
_object = (object != nil) ? RETAIN(object) : nil;
_info = (info != nil) ? RETAIN(info) : nil;
_object = TEST_RETAIN(object);
_info = TEST_RETAIN(info);
return self;
}
- (void) dealloc
{
RELEASE(_name);
if (_object)
RELEASE(_object);
if (_info)
RELEASE(_info);
TEST_RELEASE(_object);
TEST_RELEASE(_info);
[super dealloc];
}
/* Creating autoreleased Notification objects. */
+ notificationWithName: (NSString*)name
object: object
userInfo: info
+ (NSNotification*) notificationWithName: (NSString*)name
object: (id)object
userInfo: (id)info
{
return [[[self allocWithZone: NSDefaultMallocZone()] initWithName: name
object: object userInfo: info] autorelease];
}
+ notificationWithName: (NSString*)name
object: object
+ (NSNotification*) notificationWithName: (NSString*)name
object: (id)object
{
return [self notificationWithName: name object: object userInfo: nil];
}
@ -74,12 +73,12 @@
return _name;
}
- object
- (id) object
{
return _object;
}
- userInfo
- (NSDictionary*) userInfo
{
return _info;
}
@ -87,15 +86,37 @@
/* NSCopying protocol. */
- copyWithZone: (NSZone*)zone
- (id) copyWithZone: (NSZone*)zone
{
if (NSShouldRetainWithZone (self, zone))
return [self retain];
return [[[self class] allocWithZone: zone]
initWithName: _name
object: _object
userInfo: _info];
initWithName: _name
object: _object
userInfo: _info];
}
/*
* NSCoding protocol - the MacOS-X documentation says it should conform,
* but how can we meaningfully encode/decode the object and userInfo.
* We do it anyway - at least it should make sense over DO.
*/
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[super encodeWithCoder: aCoder];
[aCoder encodeValueOfObjCType: @encode(id) at: &_name];
[aCoder encodeValueOfObjCType: @encode(id) at: &_object];
[aCoder encodeValueOfObjCType: @encode(id) at: &_info];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
[super initWithCoder: aCoder];
[aCoder decodeValueOfObjCType: @encode(id) at: &_name];
[aCoder decodeValueOfObjCType: @encode(id) at: &_object];
[aCoder decodeValueOfObjCType: @encode(id) at: &_info];
return self;
}
@end

View file

@ -1,7 +1,11 @@
/* Implementation of NSNotificationCenter for GNUstep
Copyright (C) 1996 Free Software Foundation, Inc.
Copyright (C) 1999 Free Software Foundation, Inc.
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Created: June 1999
Many thanks for the earlier version, (from which this is loosely
derived) by Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Created: March 1996
This file is part of the GNUstep Base Library.
@ -10,7 +14,7 @@
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
@ -21,19 +25,871 @@
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <Foundation/NSNotification.h>
#include <base/NotificationDispatcher.h>
#include <base/behavior.h>
#include <Foundation/NSException.h>
#include <Foundation/NSMapTable.h>
#include <Foundation/NSHashTable.h>
#include <Foundation/NSLock.h>
/*
* Garbage collection considerations -
* The notification center is not supposed to retain any notification
* observers or notification objects. To achieve this when using garbage
* collection, we must hide all references to observers and objects.
* Within an Observation structure, this is not a problem, we simply
* allocate the structure using 'atomic' allocation to tell the gc
* system to ignore pointers inside it.
* Elsewhere, we store the pointers with a bit added, to hide them from
* the garbage collector.
*/
/*
* Observation structure - One of these objects is created for
* each -addObserver... request. It holds the requested selector,
* name and object. Each struct is placed
* (1) in one LinkedList, as keyed by the NAME/OBJECT parameters
* (2) in an array, as keyed by the OBSERVER
*/
typedef struct Obs {
NSString *name;
id object;
id observer;
SEL selector;
IMP method;
struct Obs *next;
unsigned retained;
} Observation;
#define ENDOBS ((Observation*)-1)
static void FreeObs(Observation *o)
{
if (o->retained)
o->retained--;
else
NSZoneFree(NSDefaultMallocZone(), o);
}
static void FreeList(Observation *list)
{
while (list != ENDOBS)
{
Observation *o = list;
list = o->next;
FreeObs(o);
}
}
static void *RetainObs(Observation *o)
{
o->retained++;
}
static unsigned oHash(void* t, Observation *o)
{
unsigned hash;
hash = (unsigned)(gsaddr)o->object ^ (unsigned)(gsaddr)o->selector;
if (o->name != nil)
hash ^= [o->name hash];
return hash;
}
static BOOL oIsEqual(void* t, Observation *o1, Observation* o2)
{
if (o1->object != o2->object)
return NO;
if (o1->selector != o2->selector)
return NO;
if (o1->name != o2->name)
return [o1->name isEqual: o2->name];
return YES;
}
static void* oRetain(void* t, Observation *o)
{
o->retained++;
}
static void oRelease(void* t, Observation *o)
{
if (o->retained)
o->retained--;
else
NSZoneFree(NSDefaultMallocZone(), o);
}
const NSHashTableCallBacks ObsCallBacks =
{
(NSHT_hash_func_t) oHash,
(NSHT_isEqual_func_t) oIsEqual,
(NSHT_retain_func_t) oRetain,
(NSHT_release_func_t) oRelease,
(NSHT_describe_func_t) 0
};
const NSMapTableValueCallBacks ObsMapCallBacks =
{
(NSMT_retain_func_t) oRetain,
(NSMT_release_func_t) oRelease,
(NSMT_describe_func_t) 0
};
/*
* Setup for inline operation on arrays of Observers.
*/
#define FAST_ARRAY_TYPES 0
#define FAST_ARRAY_EXTRA Observation*
#define FAST_ARRAY_RELEASE(X) FreeObs(((X).ext))
#define FAST_ARRAY_RETAIN(X) RetainObs(((X).ext))
#include <base/FastArray.x>
#define FAST_MAP_RETAIN_VAL(X) X
#define FAST_MAP_RELEASE_VAL(X)
#define FAST_MAP_KTYPES GSUNION_OBJ
#define FAST_MAP_VTYPES GSUNION_PTR
#include <base/FastMap.x>
#if GS_WITH_GC
/*
* In order to hide pointers from garbage collection, we OR in an
* extra bit. This should be ok for the objects we deal with
* which are all aligned on 4 or 8 byte boundaries on all the machines
* I know of.
*/
#define CHEATGC(X) (void*)(gsaddr)((X) | 1)
#else
#define CHEATGC(X) (void*)(gsaddr)(X)
#endif
@implementation NSNotificationCenter
/* This class is fully implemented in GNU's NotificationDispatcher. */
/* The default instance, most often the only one created.
It is accessed by the class methods at the end of this file.
There is no need to mutex locking of this variable. */
static NSNotificationCenter *default_center = nil;
static SEL remSel = @selector(_removeObservationFromList:);
static void (*remImp)(NSNotificationCenter*, SEL, Observation*) = 0;
+ (void) initialize
{
if (self == [NSNotificationCenter class])
class_add_behavior (self, [NotificationDispatcher class]);
{
/*
* Do alloc and init separately so the default center can refer to
* the 'default_center' variable during initialisation.
*/
default_center = [self alloc];
[default_center init];
remImp = (void (*)(NSNotificationCenter*, SEL, Observation*))
[self instanceMethodForSelector: remSel];
}
}
+ (NSNotificationCenter*) defaultCenter
{
return default_center;
}
/* Initializing. */
- (id) init
{
[super init];
wildcard = ENDOBS;
nameless = NSCreateMapTable(NSNonOwnedPointerOrNullMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
observers = NSCreateMapTable(NSNonOwnedPointerOrNullMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
named = NSZoneMalloc(NSDefaultMallocZone(), sizeof(FastMapTable_t));
FastMapInitWithZoneAndCapacity((FastMapTable)named,NSDefaultMallocZone(),128);
_lock = [NSRecursiveLock new];
return self;
}
- (void) dealloc
{
[self gcFinalize];
TEST_RELEASE(_lock);
[super dealloc];
}
- (void) gcFinalize
{
NSMapEnumerator enumerator;
id o;
FastMapTable f = (FastMapTable)named;
FastMapNode n;
Observation *l;
NSHashTable *h;
NSMapTable *m;
/*
* Free observations without notification names or numbers.
*/
FreeList(wildcard);
/*
* Free lists of observations without notification names.
*/
enumerator = NSEnumerateMapTable(nameless);
while (NSNextMapEnumeratorPair(&enumerator, (void**)&o, (void**)&l))
{
FreeList(l);
}
NSFreeMapTable(nameless);
/*
* Free lists of observations keyed by name and observer.
*/
n = f->firstNode;
while (n != 0)
{
NSFreeMapTable((NSMapTable*)n->value.ptr);
n = n->nextInMap;
}
FastMapEmpty(f);
NSZoneFree(f->zone, named);
/*
* Free tables of observations keyed by observer.
*/
enumerator = NSEnumerateMapTable(observers);
while (NSNextMapEnumeratorPair(&enumerator, (void**)&o, (void**)&h))
{
NSFreeHashTable(h);
}
NSFreeMapTable(observers);
}
/* Adding new observers. */
- (void) addObserver: (id)observer
selector: (SEL)selector
name: (NSString*)name
object: (id)object
{
NSHashTable *h;
Observation *o;
unsigned i;
IMP m;
if (observer == nil)
[NSException raise: NSInvalidArgumentException
format: @"Nil observer passed to addObserver ..."];
if (selector == 0)
[NSException raise: NSInvalidArgumentException
format: @"Null selector passed to addObserver ..."];
#if defined(DEBUG)
if ([observer respondsToSelector: selector] == NO)
NSLog(@"Observer '%@' does not respond to selector '%@'", observer,
NSStringFromSelector(selector));
#endif
m = [observer methodForSelector: selector];
if (m == 0)
[NSException raise: NSInvalidArgumentException
format: @"Observer can not handle specified selector"];
/*
* NB. Do Atomic malloc for garbage collection - so objects pointed to by
* the Observation structure will be garbage collected.
*/
#if GS_WITH_GC
o = (Observation*)NSZoneMallocAtomic(NSDefaultMallocZone(),
sizeof(Observation));
#else
o = (Observation*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(Observation));
#endif
o->name = name;
o->object = object;
o->selector = selector;
o->method = m;
o->observer = observer;
o->retained = 0;
o->next = 0;
[_lock lock];
/* Record the Observation one of the linked lists */
if (name)
{
NSMapTable *m;
Observation *list;
FastMapNode n;
/*
* Locate the map table for this name - create it if not present.
*/
n = FastMapNodeForKey((FastMapTable)named, (FastMapKey)name);
if (n == 0)
{
m = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
ObsMapCallBacks, 0);
/*
* If this is the first observation for the given name, we take a
* copy of the name so it cannot be mutated while in the map.
*/
name = [name copyWithZone: NSDefaultMallocZone()];
o->name = name;
FastMapAddPair((FastMapTable)named, (FastMapKey)name,
(FastMapVal)(void*)m);
RELEASE(name);
}
else
{
m = (NSMapTable*)n->value.ptr;
/*
* We record the name string that is used as the map key, so we
* don't need to retain it in the observation.
*/
o->name = n->key.obj;
}
/*
* Add the observation to the list for the correct object.
*/
list = (Observation*)NSMapGet(m, CHEATGC(object));
if (list == 0)
{
o->next = ENDOBS;
NSMapInsert(m, CHEATGC(object), (void*)(gsaddr)o);
}
else
{
o->next = list->next;
list->next = o;
}
}
else if (object)
{
Observation *list;
list = (Observation*)NSMapGet(nameless, CHEATGC(object));
if (list == 0)
{
o->next = ENDOBS;
NSMapInsert(nameless, CHEATGC(object), (void*)(gsaddr)o);
}
else
{
o->next = list->next;
list->next = o;
}
}
else
{
o->next = wildcard;
wildcard = o;
}
/*
* Record the notification request in a hash table keyed by OBSERVER.
* If it already exists, return without doing anything.
*/
h = (NSHashTable*)NSMapGet(observers, CHEATGC(observer));
if (h == 0)
{
h = NSCreateHashTableWithZone(ObsCallBacks, 4, NSDefaultMallocZone());
NSMapInsert(observers, CHEATGC(observer), (void*)h);
}
if (NSHashGet(h, (void*)o) != 0)
{
NSZoneFree(NSDefaultMallocZone(), o);
[_lock unlock];
return;
}
NSHashInsert(h, (void*)o);
[_lock unlock];
}
/*
* Method for internal use only.
*/
- (void) _removeObservationFromList: (Observation*)o
{
NSAssert(o->next != 0, NSInternalInconsistencyException);
/* Remove the Observation from its list */
if (o->name)
{
NSMapTable *m;
Observation *list;
FastMapNode n;
/*
* Locate the map table for this name.
*/
n = FastMapNodeForKey((FastMapTable)named, (FastMapKey)o->name);
NSAssert(n != 0, NSInternalInconsistencyException);
m = (NSMapTable*)n->value.ptr;
list = (Observation*)NSMapGet(m, CHEATGC(o->object));
NSAssert(list != 0, NSInternalInconsistencyException);
if (list == o)
{
if (list->next == ENDOBS)
{
NSMapRemove(m, CHEATGC(o->object));
if (NSCountMapTable(m) == 0)
{
FastMapRemoveKey((FastMapTable)named, (FastMapKey)o->name);
}
}
else
{
NSMapInsert(m, CHEATGC(o->object), (void*)(gsaddr)o->next);
}
}
else
{
while (list->next != o)
{
list = list->next;
}
list->next = o->next;
}
}
else if (o->object)
{
Observation *list;
list = (Observation*)NSMapGet(nameless, CHEATGC(o->object));
NSAssert(list != 0, NSInternalInconsistencyException);
if (list == o)
{
if (list->next == ENDOBS)
{
NSMapRemove(nameless, CHEATGC(o->object));
}
else
{
NSMapInsert(nameless, CHEATGC(o->object),
(void*)(gsaddr)o->next);
}
}
else
{
while (list->next != o)
{
list = list->next;
}
list->next = o->next;
}
}
else
{
if (wildcard == o)
{
wildcard = o->next;
}
else
{
Observation *list = wildcard;
while (list->next != o)
{
list = list->next;
}
list->next = o->next;
}
}
/*
* Mark this observation as not being in a list.
*/
o->next = 0;
}
/* Remove all records pertaining to OBSERVER. For instance, this
should be called before the OBSERVER is -dealloc'ed. */
- (void) removeObserver: (id)observer
{
NSHashEnumerator enumerator;
NSHashTable *h;
Observation *obs;
if (observer == nil)
[NSException raise: NSInvalidArgumentException
format: @"Nil observer passed to removeObserver:"];
[_lock lock];
h = (NSHashTable*)NSMapGet(observers, CHEATGC(observer));
if (h == 0)
return;
enumerator = NSEnumerateHashTable(h);
while ((obs = (Observation*)NSNextHashEnumeratorItem(&enumerator)) != 0)
{
(*remImp)(self, remSel, obs);
}
NSFreeHashTable(h);
NSMapRemove(observers, CHEATGC(observer));
[_lock unlock];
}
/* Remove the notification requests for the given parameters. As with
adding an observation request, nil NAME or OBJECT act as wildcards. */
- (void) removeObserver: (id)observer
name: (NSString*)name
object: (id)object
{
FastArray a;
/*
* If both NAME and OBJECT are nil, this call is the same as
* -removeObserver:, so just call it.
*/
if (name == nil && object == nil)
{
[self removeObserver: observer];
return;
}
/* We are now guaranteed that at least one of NAME and OBJECT is non-nil. */
[_lock lock];
a = NSZoneMalloc(NSDefaultMallocZone(), sizeof(FastArray_t));
FastArrayInitWithZoneAndCapacity(a, NSDefaultMallocZone(), 128);
if (name)
{
NSMapTable *m;
FastMapNode n;
/*
* Locate items with specified name (if any).
*/
n = FastMapNodeForKey((FastMapTable)named, (FastMapKey)name);
if (n)
m = (NSMapTable*)n->value.ptr;
else
m = 0;
if (m != 0)
{
if (object == nil)
{
Observation *list;
NSMapEnumerator e;
id o;
/*
* Make a list of items for ALL objects.
*/
e = NSEnumerateMapTable(m);
while (NSNextMapEnumeratorPair(&e, (void**)&o, (void**)&list))
{
while (list != ENDOBS)
{
if (observer == nil || observer == list->observer)
{
FastArrayAddItem(a, (FastArrayItem)list);
}
list = list->next;
}
}
}
else
{
Observation *list;
/*
* Make a list of items matching specific object.
*/
list = (Observation*)NSMapGet(m, CHEATGC(object));
if (list != 0)
{
while (list != ENDOBS)
{
if (observer == nil || observer == list->observer)
{
FastArrayAddItem(a, (FastArrayItem)list);
}
list = list->next;
}
}
}
}
}
else
{
Observation *list;
NSMapTable *m;
FastMapNode n;
/*
* Make a list of items matching specific object with NO names
*/
list = (Observation*)NSMapGet(nameless, CHEATGC(object));
if (list != 0)
{
while (list != ENDOBS)
{
if (observer == nil || observer == list->observer)
{
FastArrayAddItem(a, (FastArrayItem)list);
}
list = list->next;
}
}
/*
* Add items for ALL names.
*/
n = ((FastMapTable)named)->firstNode;
while (n != 0)
{
m = (NSMapTable*)n->value.ptr;
n = n->nextInMap;
list = (Observation*)NSMapGet(m, CHEATGC(object));
if (list != 0)
{
while (list != ENDOBS)
{
if (observer == nil || observer == list->observer)
{
FastArrayAddItem(a, (FastArrayItem)list);
}
list = list->next;
}
}
}
}
if (FastArrayCount(a) > 0)
{
id lastObs = nil;
NSHashTable *h = 0;
unsigned count = FastArrayCount(a);
unsigned i;
Observation *o;
for (i = 0; i < count; i++)
{
o = FastArrayItemAtIndex(a, i).ext;
(*remImp)(self, remSel, o);
if (h == 0 || lastObs != o->observer)
{
h = (NSHashTable*)NSMapGet(observers, CHEATGC(o->observer));
lastObs = o->observer;
}
NSHashRemove(h, (void*)o);
if (NSCountHashTable(h) == 0)
{
NSMapRemove(observers, CHEATGC(lastObs));
}
}
}
FastArrayEmpty(a);
NSZoneFree(a->zone, (void*)a);
[_lock unlock];
}
/*
* Post NOTIFICATION to all the observers that match its NAME and OBJECT.
*
* For performance reasons, we don't wrap an exception handler round every
* message sent to an observer. This means that, if one observer raises
* an exception, later observers in the lists will not get the notification.
*/
- (void) postNotification: (NSNotification*)notification
{
NSString *n_name;
id n_object;
Observation *o;
FastArray a;
unsigned count;
unsigned i;
if (notification == nil)
[NSException raise: NSInvalidArgumentException
format: @"Tried to post a nil notification."];
n_name = [notification name];
n_object = [notification object];
if (n_name == nil)
[NSException raise: NSInvalidArgumentException
format: @"Tried to post a notification with no name."];
[_lock lock];
a = NSZoneMalloc(NSDefaultMallocZone(), sizeof(FastArray_t));
FastArrayInitWithZoneAndCapacity(a, NSDefaultMallocZone(), 16);
NS_DURING
{
/*
* Post the notification to all the observers that specified neither
* NAME nor OBJECT.
*/
for (o = wildcard; o != ENDOBS; o = o->next)
{
FastArrayAddItem(a, (FastArrayItem)o);
}
count = FastArrayCount(a);
while (count-- > 0)
{
o = FastArrayItemAtIndex(a, count).ext;
if (o->next != 0)
(*o->method)(o->observer, o->selector, notification);
FastArrayRemoveItemAtIndex(a, count);
}
/*
* Post the notification to all the observers that specified OBJECT,
* but didn't specify NAME.
*/
if (n_object)
{
o = (Observation*)NSMapGet(nameless, CHEATGC(n_object));
if (o != 0)
{
while (o != ENDOBS)
{
FastArrayAddItem(a, (FastArrayItem)o);
o = o->next;
}
count = FastArrayCount(a);
while (count-- > 0)
{
o = FastArrayItemAtIndex(a, count).ext;
if (o->next != 0)
(*o->method)(o->observer, o->selector, notification);
FastArrayRemoveItemAtIndex(a, count);
}
}
}
/*
* Post the notification to all the observers of NAME, except those
* observers with a non-nill OBJECT that doesn't match the
* notification's OBJECT).
*/
if (n_name)
{
NSMapTable *m;
FastMapNode n;
n = FastMapNodeForKey((FastMapTable)named, (FastMapKey)n_name);
if (n)
m = (NSMapTable*)n->value.ptr;
else
m = 0;
if (m != 0)
{
/*
* First, observers with a matching object.
*/
o = (Observation*)NSMapGet(m, CHEATGC(n_object));
if (o != 0)
{
while (o != ENDOBS)
{
FastArrayAddItem(a, (FastArrayItem)o);
o = o->next;
}
count = FastArrayCount(a);
while (count-- > 0)
{
o = FastArrayItemAtIndex(a, count).ext;
if (o->next != 0)
(*o->method)(o->observer, o->selector, notification);
FastArrayRemoveItemAtIndex(a, count);
}
}
if (n_object != nil)
{
/*
* Now observers with a nil object.
*/
o = (Observation*)NSMapGet(m, CHEATGC(0));
if (o != 0)
{
while (o != ENDOBS)
{
FastArrayAddItem(a, (FastArrayItem)o);
o = o->next;
}
count = FastArrayCount(a);
while (count-- > 0)
{
o = FastArrayItemAtIndex(a, count).ext;
if (o->next != 0)
(*o->method)(o->observer, o->selector,
notification);
FastArrayRemoveItemAtIndex(a, count);
}
}
}
}
}
}
NS_HANDLER
{
/*
* If we had a problem - release memory and unlock before going on.
*/
FastArrayEmpty(a);
NSZoneFree(a->zone, (void*)a);
[_lock unlock];
[localException raise];
}
NS_ENDHANDLER
FastArrayEmpty(a);
NSZoneFree(a->zone, (void*)a);
[_lock unlock];
}
- (void) postNotificationName: (NSString*)name
object: (id)object
{
[self postNotification: [NSNotification notificationWithName: name
object: object]];
}
- (void) postNotificationName: (NSString*)name
object: (id)object
userInfo: (NSDictionary*)info
{
[self postNotification: [NSNotification notificationWithName: name
object: object
userInfo: info]];
}
@end