2001-12-17 14:31:42 +00:00
|
|
|
|
/** Implementation of NSNotificationCenter for GNUstep
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
2023-12-23 01:44:50 +00:00
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Created: June 1999
|
|
|
|
|
|
|
|
|
|
Many thanks for the earlier version, (from which this is loosely
|
|
|
|
|
derived) by Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
1996-03-03 01:41:20 +00:00
|
|
|
|
Created: March 1996
|
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1996-02-13 16:09:50 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
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
|
2019-12-09 23:36:00 +00:00
|
|
|
|
Lesser General Public License for more details.
|
1996-03-03 01:41:20 +00:00
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1996-02-13 16:09:50 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2006-10-20 10:56:27 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
2019-12-09 23:36:00 +00:00
|
|
|
|
Boston, MA 02110 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSNotificationCenter class reference</title>
|
1996-03-03 01:41:20 +00:00
|
|
|
|
*/
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#define EXPOSE_NSNotificationCenter_IVARS 1
|
2009-01-13 15:57:38 +00:00
|
|
|
|
#import "Foundation/NSNotification.h"
|
2012-12-08 22:16:20 +00:00
|
|
|
|
#import "Foundation/NSDictionary.h"
|
2009-01-13 15:57:38 +00:00
|
|
|
|
#import "Foundation/NSException.h"
|
|
|
|
|
#import "Foundation/NSLock.h"
|
2014-02-14 14:23:09 +00:00
|
|
|
|
#import "Foundation/NSOperation.h"
|
2009-01-13 15:57:38 +00:00
|
|
|
|
#import "Foundation/NSThread.h"
|
2024-04-30 16:44:26 +00:00
|
|
|
|
#import "Foundation/NSHashTable.h"
|
2009-01-13 15:57:38 +00:00
|
|
|
|
|
2009-01-19 11:00:33 +00:00
|
|
|
|
static NSZone *_zone = 0;
|
|
|
|
|
|
2002-08-07 13:29:31 +00:00
|
|
|
|
/**
|
2004-06-22 22:40:40 +00:00
|
|
|
|
* Concrete class implementing NSNotification.
|
2002-08-07 13:29:31 +00:00
|
|
|
|
*/
|
|
|
|
|
@interface GSNotification : NSNotification
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
NSString *_name;
|
|
|
|
|
id _object;
|
|
|
|
|
NSDictionary *_info;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSNotification
|
|
|
|
|
|
|
|
|
|
static Class concrete = 0;
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (concrete == 0)
|
|
|
|
|
{
|
|
|
|
|
concrete = [GSNotification class];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (NSNotification*) notificationWithName: (NSString*)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
userInfo: (NSDictionary*)info
|
|
|
|
|
{
|
|
|
|
|
GSNotification *n;
|
|
|
|
|
|
|
|
|
|
n = (GSNotification*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
2010-03-05 09:30:18 +00:00
|
|
|
|
n->_name = [name copyWithZone: [self zone]];
|
2002-08-07 13:29:31 +00:00
|
|
|
|
n->_object = TEST_RETAIN(object);
|
|
|
|
|
n->_info = TEST_RETAIN(info);
|
|
|
|
|
return AUTORELEASE(n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
|
|
|
{
|
|
|
|
|
GSNotification *n;
|
|
|
|
|
|
|
|
|
|
if (NSShouldRetainWithZone (self, zone))
|
|
|
|
|
{
|
|
|
|
|
return [self retain];
|
|
|
|
|
}
|
2013-04-08 08:33:12 +00:00
|
|
|
|
n = (GSNotification*)NSAllocateObject(concrete, 0, zone);
|
2010-03-05 09:30:18 +00:00
|
|
|
|
n->_name = [_name copyWithZone: [self zone]];
|
2002-08-07 13:29:31 +00:00
|
|
|
|
n->_object = TEST_RETAIN(_object);
|
|
|
|
|
n->_info = TEST_RETAIN(_info);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
RELEASE(_name);
|
|
|
|
|
TEST_RELEASE(_object);
|
|
|
|
|
TEST_RELEASE(_info);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString*) name
|
|
|
|
|
{
|
|
|
|
|
return _name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) object
|
|
|
|
|
{
|
|
|
|
|
return _object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSDictionary*) userInfo
|
|
|
|
|
{
|
|
|
|
|
return _info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
struct NCTbl; /* Notification Center Table structure */
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Observation structure - One of these objects is created for
|
|
|
|
|
* each -addObserver... request. It holds the requested selector,
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* name and object. Each struct is placed in one LinkedList,
|
|
|
|
|
* as keyed by the NAME/OBJECT parameters.
|
2008-05-01 20:14:05 +00:00
|
|
|
|
* If 'next' is 0 then the observation is unused (ie it has been
|
|
|
|
|
* removed from, or not yet added to any list). The end of a
|
|
|
|
|
* list is marked by 'next' being set to 'ENDOBS'.
|
2011-05-31 06:46:17 +00:00
|
|
|
|
*
|
|
|
|
|
* This is normally a structure which handles memory management using a fast
|
|
|
|
|
* reference count mechanism, but when built with clang for GC, a structure
|
|
|
|
|
* can't hold a zeroing weak pointer to an observer so it's implemented as a
|
|
|
|
|
* trivial class instead ... and gets managed by the garbage collector.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
typedef struct Obs {
|
|
|
|
|
id observer; /* Object to receive message. */
|
|
|
|
|
SEL selector; /* Method selector. */
|
|
|
|
|
struct Obs *next; /* Next item in linked list. */
|
|
|
|
|
int retained; /* Retain count for structure. */
|
|
|
|
|
struct NCTbl *link; /* Pointer back to chunk table */
|
|
|
|
|
} Observation;
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
#define ENDOBS ((Observation*)-1)
|
|
|
|
|
|
2013-07-01 11:37:58 +00:00
|
|
|
|
static inline NSUInteger doHash(BOOL shouldHash, NSString* key)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (key == nil)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-07-01 11:37:58 +00:00
|
|
|
|
else if (NO == shouldHash)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
2013-07-01 11:37:58 +00:00
|
|
|
|
return (NSUInteger)(uintptr_t)key;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
else
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
|
|
|
|
return [key hash];
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-01 11:37:58 +00:00
|
|
|
|
static inline BOOL doEqual(BOOL shouldHash, NSString* key1, NSString* key2)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (key1 == key2)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
2013-07-01 11:37:58 +00:00
|
|
|
|
else if (NO == shouldHash)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-10-09 04:41:18 +00:00
|
|
|
|
return [key1 isEqualToString: key2];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup for inline operation on arrays of Observers.
|
|
|
|
|
*/
|
|
|
|
|
static void listFree(Observation *list);
|
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
/* Observations have retain/release counts managed explicitly by fast
|
|
|
|
|
* function calls.
|
|
|
|
|
*/
|
|
|
|
|
static void obsRetain(Observation *o);
|
|
|
|
|
static void obsFree(Observation *o);
|
|
|
|
|
|
2002-01-31 07:20:16 +00:00
|
|
|
|
|
2002-01-31 07:24:27 +00:00
|
|
|
|
#define GSI_ARRAY_TYPES 0
|
|
|
|
|
#define GSI_ARRAY_TYPE Observation*
|
2011-05-31 06:46:17 +00:00
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X) obsFree(X.ext)
|
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X) obsRetain(X.ext)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIArray.h"
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
|
#define GSI_MAP_RETAIN_KEY(M, X)
|
2013-05-13 07:09:41 +00:00
|
|
|
|
#define GSI_MAP_RELEASE_KEY(M, X) ({if (YES == M->extra) RELEASE(X.obj);})
|
2013-07-01 11:37:58 +00:00
|
|
|
|
#define GSI_MAP_HASH(M, X) doHash(M->extra, X.obj)
|
|
|
|
|
#define GSI_MAP_EQUAL(M, X,Y) doEqual(M->extra, X.obj, Y.obj)
|
2005-02-22 11:22:44 +00:00
|
|
|
|
#define GSI_MAP_RETAIN_VAL(M, X)
|
2002-01-24 17:03:04 +00:00
|
|
|
|
#define GSI_MAP_RELEASE_VAL(M, X)
|
|
|
|
|
|
2011-03-29 08:16:02 +00:00
|
|
|
|
#define GSI_MAP_KTYPES GSUNION_OBJ|GSUNION_NSINT
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define GSI_MAP_VTYPES GSUNION_PTR
|
|
|
|
|
#define GSI_MAP_VEXTRA Observation*
|
2013-05-13 07:09:41 +00:00
|
|
|
|
#define GSI_MAP_EXTRA BOOL
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIMap.h"
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* An NC table is used to keep track of memory allocated to store
|
|
|
|
|
* Observation structures. When an Observation is removed from the
|
|
|
|
|
* notification center, it's memory is returned to the free list of
|
|
|
|
|
* the chunk table, rather than being released to the general
|
|
|
|
|
* memory allocation system. This means that, once a large numbner
|
|
|
|
|
* of observers have been registered, memory usage will never shrink
|
|
|
|
|
* even if the observers are removed. On the other hand, the process
|
|
|
|
|
* of adding and removing observers is speeded up.
|
|
|
|
|
*
|
|
|
|
|
* As another minor aid to performance, we also maintain a cache of
|
|
|
|
|
* the map tables used to keep mappings of notification objects to
|
|
|
|
|
* lists of Observations. This lets us avoid the overhead of creating
|
|
|
|
|
* and destroying map tables when we are frequently adding and removing
|
|
|
|
|
* notification observations.
|
|
|
|
|
*/
|
|
|
|
|
#define CHUNKSIZE 128
|
|
|
|
|
#define CACHESIZE 16
|
|
|
|
|
typedef struct NCTbl {
|
|
|
|
|
Observation *wildcard; /* Get ALL messages. */
|
|
|
|
|
GSIMapTable nameless; /* Get messages for any name. */
|
|
|
|
|
GSIMapTable named; /* Getting named messages only. */
|
|
|
|
|
unsigned lockCount; /* Count recursive operations. */
|
2011-05-31 06:46:17 +00:00
|
|
|
|
NSRecursiveLock *_lock; /* Lock out other threads. */
|
2006-09-13 10:20:49 +00:00
|
|
|
|
Observation *freeList;
|
|
|
|
|
Observation **chunks;
|
|
|
|
|
unsigned numChunks;
|
|
|
|
|
GSIMapTable cache[CACHESIZE];
|
2003-01-03 20:14:47 +00:00
|
|
|
|
unsigned short chunkIndex;
|
|
|
|
|
unsigned short cacheIndex;
|
2024-04-30 16:44:26 +00:00
|
|
|
|
NSHashTable *retainedObjectsTable;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
} NCTable;
|
|
|
|
|
|
1999-09-16 07:21:34 +00:00
|
|
|
|
#define TABLE ((NCTable*)_table)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#define WILDCARD (TABLE->wildcard)
|
|
|
|
|
#define NAMELESS (TABLE->nameless)
|
|
|
|
|
#define NAMED (TABLE->named)
|
|
|
|
|
#define LOCKCOUNT (TABLE->lockCount)
|
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
static Observation *
|
2013-05-13 07:09:41 +00:00
|
|
|
|
obsNew(NCTable *t, SEL s, id o)
|
2011-05-28 12:49:52 +00:00
|
|
|
|
{
|
2011-05-31 06:46:17 +00:00
|
|
|
|
Observation *obs;
|
2011-05-28 12:49:52 +00:00
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
/* Generally, observations are cached and we create a 'new' observation
|
|
|
|
|
* by retrieving from the cache or by allocating a block of observations
|
2022-02-17 10:08:18 +00:00
|
|
|
|
* in one go. This works nicely to provide high
|
2011-05-31 06:46:17 +00:00
|
|
|
|
* performance for situations where apps add/remove lots of observers
|
|
|
|
|
* very frequently (poor design, but something which happens in the
|
|
|
|
|
* real world unfortunately).
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->freeList == 0)
|
|
|
|
|
{
|
|
|
|
|
Observation *block;
|
|
|
|
|
|
|
|
|
|
if (t->chunkIndex == CHUNKSIZE)
|
|
|
|
|
{
|
|
|
|
|
unsigned size;
|
|
|
|
|
|
|
|
|
|
t->numChunks++;
|
2009-02-04 20:19:27 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
size = t->numChunks * sizeof(Observation*);
|
2009-02-04 20:19:27 +00:00
|
|
|
|
t->chunks = (Observation**)NSReallocateCollectable(
|
|
|
|
|
t->chunks, size, NSScannedOption);
|
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
size = CHUNKSIZE * sizeof(Observation);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->chunks[t->numChunks - 1]
|
2009-03-09 15:11:51 +00:00
|
|
|
|
= (Observation*)NSAllocateCollectable(size, 0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->chunkIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
block = t->chunks[t->numChunks - 1];
|
2011-05-31 06:46:17 +00:00
|
|
|
|
t->freeList = &block[t->chunkIndex];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->chunkIndex++;
|
|
|
|
|
t->freeList->link = 0;
|
|
|
|
|
}
|
|
|
|
|
obs = t->freeList;
|
|
|
|
|
t->freeList = (Observation*)obs->link;
|
|
|
|
|
obs->link = (void*)t;
|
2011-05-31 06:46:17 +00:00
|
|
|
|
obs->retained = 0;
|
|
|
|
|
obs->next = 0;
|
|
|
|
|
|
|
|
|
|
obs->selector = s;
|
|
|
|
|
obs->observer = o;
|
|
|
|
|
|
|
|
|
|
return obs;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static GSIMapTable mapNew(NCTable *t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->cacheIndex > 0)
|
2011-05-31 06:46:17 +00:00
|
|
|
|
{
|
|
|
|
|
return t->cache[--t->cacheIndex];
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSIMapTable m;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2009-02-04 17:41:19 +00:00
|
|
|
|
m = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
2009-03-09 15:11:51 +00:00
|
|
|
|
GSIMapInitWithZoneAndCapacity(m, _zone, 2);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
return m;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void mapFree(NCTable *t, GSIMapTable m)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (t->cacheIndex < CACHESIZE)
|
2009-02-04 20:19:27 +00:00
|
|
|
|
{
|
|
|
|
|
t->cache[t->cacheIndex++] = m;
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSIMapEmptyMap(m);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void endNCTable(NCTable *t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
GSIMapEnumerator_t e0;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapNode n0;
|
|
|
|
|
Observation *l;
|
|
|
|
|
|
2011-06-19 09:26:03 +00:00
|
|
|
|
TEST_RELEASE(t->_lock);
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Free observations without notification names or numbers.
|
|
|
|
|
*/
|
|
|
|
|
listFree(t->wildcard);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free lists of observations without notification names.
|
|
|
|
|
*/
|
2000-12-21 10:31:33 +00:00
|
|
|
|
e0 = GSIMapEnumeratorForMap(t->nameless);
|
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (n0 != 0)
|
|
|
|
|
{
|
|
|
|
|
l = (Observation*)n0->value.ptr;
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
listFree(l);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(t->nameless);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->nameless);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Free lists of observations keyed by name and observer.
|
|
|
|
|
*/
|
2000-12-21 10:31:33 +00:00
|
|
|
|
e0 = GSIMapEnumeratorForMap(t->named);
|
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (n0 != 0)
|
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapTable m = (GSIMapTable)n0->value.ptr;
|
|
|
|
|
GSIMapEnumerator_t e1 = GSIMapEnumeratorForMap(m);
|
|
|
|
|
GSIMapNode n1 = GSIMapEnumeratorNextNode(&e1);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
|
|
|
|
while (n1 != 0)
|
|
|
|
|
{
|
|
|
|
|
l = (Observation*)n1->value.ptr;
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n1 = GSIMapEnumeratorNextNode(&e1);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
listFree(l);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(m);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)m);
|
|
|
|
|
}
|
|
|
|
|
GSIMapEmptyMap(t->named);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->named);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < t->numChunks; i++)
|
2000-12-21 10:31:33 +00:00
|
|
|
|
{
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t->chunks[i]);
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
for (i = 0; i < t->cacheIndex; i++)
|
|
|
|
|
{
|
|
|
|
|
GSIMapEmptyMap(t->cache[i]);
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), (void*)t->cache[i]);
|
|
|
|
|
}
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t->chunks);
|
2024-04-30 16:44:26 +00:00
|
|
|
|
RELEASE(t->retainedObjectsTable);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), t);
|
2024-04-30 16:44:26 +00:00
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-09 16:40:00 +00:00
|
|
|
|
static NCTable *newNCTable(void)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
NCTable *t;
|
|
|
|
|
|
2009-02-04 17:41:19 +00:00
|
|
|
|
t = (NCTable*)NSAllocateCollectable(sizeof(NCTable), NSScannedOption);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->chunkIndex = CHUNKSIZE;
|
|
|
|
|
t->wildcard = ENDOBS;
|
|
|
|
|
|
2009-02-04 17:41:19 +00:00
|
|
|
|
t->nameless = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
|
|
|
|
t->named = NSAllocateCollectable(sizeof(GSIMapTable_t), NSScannedOption);
|
|
|
|
|
GSIMapInitWithZoneAndCapacity(t->nameless, _zone, 16);
|
2009-01-19 11:00:33 +00:00
|
|
|
|
GSIMapInitWithZoneAndCapacity(t->named, _zone, 128);
|
2013-05-13 07:09:41 +00:00
|
|
|
|
t->named->extra = YES; // This table retains keys
|
2024-04-30 16:44:26 +00:00
|
|
|
|
t->retainedObjectsTable = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:10];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2003-11-03 12:32:46 +00:00
|
|
|
|
t->_lock = [NSRecursiveLock new];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
return t;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static inline void lockNCTable(NCTable* t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2006-09-13 10:20:49 +00:00
|
|
|
|
[t->_lock lock];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->lockCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void unlockNCTable(NCTable* t)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
t->lockCount--;
|
2006-09-13 10:20:49 +00:00
|
|
|
|
[t->_lock unlock];
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-31 06:46:17 +00:00
|
|
|
|
static void obsFree(Observation *o)
|
|
|
|
|
{
|
|
|
|
|
NSCAssert(o->retained >= 0, NSInternalInconsistencyException);
|
|
|
|
|
if (o->retained-- == 0)
|
|
|
|
|
{
|
|
|
|
|
NCTable *t = o->link;
|
|
|
|
|
|
|
|
|
|
o->link = (NCTable*)t->freeList;
|
|
|
|
|
t->freeList = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void obsRetain(Observation *o)
|
|
|
|
|
{
|
|
|
|
|
o->retained++;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void listFree(Observation *list)
|
|
|
|
|
{
|
|
|
|
|
while (list != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
Observation *o = list;
|
|
|
|
|
|
|
|
|
|
list = o->next;
|
|
|
|
|
o->next = 0;
|
2011-05-31 06:46:17 +00:00
|
|
|
|
obsFree(o);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* NB. We need to explicitly set the 'next' field of any observation
|
|
|
|
|
* we remove to be zero so that, if it currently exists in an array
|
|
|
|
|
* of observations being posted, the posting code can notice that it
|
|
|
|
|
* has been removed from its linked list.
|
2009-02-11 09:31:30 +00:00
|
|
|
|
*
|
|
|
|
|
* Also,
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static Observation *listPurge(Observation *list, id observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *tmp;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (list != ENDOBS && list->observer == observer)
|
|
|
|
|
{
|
|
|
|
|
tmp = list->next;
|
|
|
|
|
list->next = 0;
|
2011-05-31 06:46:17 +00:00
|
|
|
|
obsFree(list);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
list = tmp;
|
|
|
|
|
}
|
|
|
|
|
if (list != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
tmp = list;
|
|
|
|
|
while (tmp->next != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
if (tmp->next->observer == observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *next = tmp->next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
tmp->next = next->next;
|
|
|
|
|
next->next = 0;
|
2011-05-31 06:46:17 +00:00
|
|
|
|
obsFree(next);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Utility function to remove all the observations from a particular
|
|
|
|
|
* map table node that match the specified observer. If the observer
|
|
|
|
|
* is nil, then all observations are removed.
|
|
|
|
|
* If the list of observations in the map node is emptied, the node is
|
|
|
|
|
* removed from the map.
|
|
|
|
|
*/
|
|
|
|
|
static inline void
|
|
|
|
|
purgeMapNode(GSIMapTable map, GSIMapNode node, id observer)
|
|
|
|
|
{
|
|
|
|
|
Observation *list = node->value.ext;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (observer == 0)
|
|
|
|
|
{
|
|
|
|
|
listFree(list);
|
|
|
|
|
GSIMapRemoveKey(map, node->key);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Observation *start = list;
|
|
|
|
|
|
|
|
|
|
list = listPurge(list, observer);
|
|
|
|
|
if (list == ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The list is empty so remove from map.
|
|
|
|
|
*/
|
|
|
|
|
GSIMapRemoveKey(map, node->key);
|
|
|
|
|
}
|
|
|
|
|
else if (list != start)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The list is not empty, but we have changed its
|
|
|
|
|
* start, so we must place the new head in the map.
|
|
|
|
|
*/
|
|
|
|
|
node->value.ext = list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2009-02-11 09:31:30 +00:00
|
|
|
|
/* purgeCollected() returns a list of observations with any observations for
|
|
|
|
|
* a collected observer removed.
|
|
|
|
|
* purgeCollectedFromMapNode() does the same thing but also handles cleanup
|
|
|
|
|
* of the map node containing the list if necessary.
|
|
|
|
|
*/
|
|
|
|
|
#define purgeCollected(X) (X)
|
|
|
|
|
#define purgeCollectedFromMapNode(X, Y) ((Observation*)Y->value.ext)
|
|
|
|
|
|
2014-02-14 14:23:09 +00:00
|
|
|
|
|
|
|
|
|
@interface GSNotificationBlockOperation : NSOperation
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
NSNotification *_notification;
|
|
|
|
|
GSNotificationBlock _block;
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithNotification: (NSNotification *)notif
|
|
|
|
|
block: (GSNotificationBlock)block;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSNotificationBlockOperation
|
|
|
|
|
|
|
|
|
|
- (id) initWithNotification: (NSNotification *)notif
|
|
|
|
|
block: (GSNotificationBlock)block
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(_notification, notif);
|
|
|
|
|
_block = Block_copy(block);
|
|
|
|
|
}
|
|
|
|
|
return self;
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
DESTROY(_notification);
|
|
|
|
|
Block_release(_block);
|
|
|
|
|
DEALLOC
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) main
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
CALL_BLOCK(_block, _notification);
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2024-01-09 08:25:28 +00:00
|
|
|
|
/* Cached class for fast test when removing observer.
|
|
|
|
|
*/
|
|
|
|
|
static Class GSNotificationObserverClass = Nil;
|
|
|
|
|
|
2014-02-14 14:23:09 +00:00
|
|
|
|
@interface GSNotificationObserver : NSObject
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
NSOperationQueue *_queue;
|
|
|
|
|
GSNotificationBlock _block;
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSNotificationObserver
|
|
|
|
|
|
2024-01-09 08:25:28 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if ([GSNotificationObserver class] == self)
|
|
|
|
|
{
|
|
|
|
|
GSNotificationObserverClass = self;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 14:23:09 +00:00
|
|
|
|
- (id) initWithQueue: (NSOperationQueue *)queue
|
|
|
|
|
block: (GSNotificationBlock)block
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(_queue, queue);
|
|
|
|
|
_block = Block_copy(block);
|
|
|
|
|
}
|
|
|
|
|
return self;
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
DESTROY(_queue);
|
|
|
|
|
Block_release(_block);
|
|
|
|
|
DEALLOC
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) didReceiveNotification: (NSNotification *)notif
|
|
|
|
|
{
|
2023-11-14 21:20:50 +00:00
|
|
|
|
if (_queue != nil)
|
|
|
|
|
{
|
|
|
|
|
GSNotificationBlockOperation *op = [[GSNotificationBlockOperation alloc]
|
|
|
|
|
initWithNotification: notif block: _block];
|
2014-02-14 14:23:09 +00:00
|
|
|
|
|
2023-11-14 21:20:50 +00:00
|
|
|
|
[_queue addOperation: op];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CALL_BLOCK(_block, notif);
|
|
|
|
|
}
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>GNUstep provides a framework for sending messages between objects within
|
|
|
|
|
* a process called <em>notifications</em>. Objects register with an
|
|
|
|
|
* <code>NSNotificationCenter</code> to be informed whenever other objects
|
|
|
|
|
* post [NSNotification]s to it matching certain criteria. The notification
|
|
|
|
|
* center processes notifications synchronously -- that is, control is only
|
|
|
|
|
* returned to the notification poster once every recipient of the
|
|
|
|
|
* notification has received it and processed it. Asynchronous processing is
|
|
|
|
|
* possible using an [NSNotificationQueue].</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>Obtain an instance using the +defaultCenter method.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>In a multithreaded process, notifications are always sent on the thread
|
|
|
|
|
* that they are posted from.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>Use the [NSDistributedNotificationCenter] for interprocess
|
|
|
|
|
* communications on the same machine.</p>
|
|
|
|
|
*/
|
1996-02-13 16:09:50 +00:00
|
|
|
|
@implementation NSNotificationCenter
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/* 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;
|
1996-02-13 16:09:50 +00:00
|
|
|
|
|
2011-06-19 09:26:03 +00:00
|
|
|
|
+ (void) atExit
|
|
|
|
|
{
|
|
|
|
|
id tmp = default_center;
|
|
|
|
|
|
|
|
|
|
default_center = nil;
|
|
|
|
|
[tmp release];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-03 01:41:20 +00:00
|
|
|
|
+ (void) initialize
|
1996-02-13 16:09:50 +00:00
|
|
|
|
{
|
1996-03-03 01:41:20 +00:00
|
|
|
|
if (self == [NSNotificationCenter class])
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2009-01-19 11:00:33 +00:00
|
|
|
|
_zone = NSDefaultMallocZone();
|
2002-08-07 13:29:31 +00:00
|
|
|
|
if (concrete == 0)
|
|
|
|
|
{
|
|
|
|
|
concrete = [GSNotification class];
|
|
|
|
|
}
|
2024-03-11 14:36:22 +00:00
|
|
|
|
/* Ensure value is initialised before we use it in
|
|
|
|
|
* -removeObserver:name:object:
|
|
|
|
|
*/
|
|
|
|
|
if (nil == GSNotificationObserverClass)
|
|
|
|
|
{
|
|
|
|
|
[GSNotificationObserver class];
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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];
|
2011-06-19 13:34:17 +00:00
|
|
|
|
[self registerAtExit];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the default notification center being used for this task (process).
|
|
|
|
|
* This is used for all notifications posted by the Base library unless
|
|
|
|
|
* otherwise noted.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
+ (NSNotificationCenter*) defaultCenter
|
|
|
|
|
{
|
|
|
|
|
return default_center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Initializing. */
|
|
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
|
{
|
2003-11-03 06:27:52 +00:00
|
|
|
|
if ((self = [super init]) != nil)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
2004-02-04 00:14:00 +00:00
|
|
|
|
_table = newNCTable();
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
2009-01-12 18:36:37 +00:00
|
|
|
|
[self finalize];
|
2024-04-30 16:44:26 +00:00
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-12 18:36:37 +00:00
|
|
|
|
- (void) finalize
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2004-11-10 11:45:08 +00:00
|
|
|
|
if (self == default_center)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"Attempt to destroy the default center"];
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Release all memory used to store Observations etc.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
endNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Adding new observers. */
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Registers observer to receive notifications with the name
|
|
|
|
|
* notificationName and/or containing object (one or both of these two must be
|
|
|
|
|
* non-nil; nil acts like a wildcard). When a notification of name name
|
|
|
|
|
* containing object is posted, observer receives a selector message with this
|
|
|
|
|
* notification as the argument. The notification center waits for the
|
|
|
|
|
* observer to finish processing the message, then informs the next registree
|
|
|
|
|
* matching the notification, and after all of this is done, control returns
|
|
|
|
|
* to the poster of the notification. Therefore the processing in the
|
|
|
|
|
* selector implementation should be short.</p>
|
2005-02-22 11:22:44 +00:00
|
|
|
|
*
|
2004-06-22 22:40:40 +00:00
|
|
|
|
* <p>The notification center does not retain observer or object. Therefore,
|
|
|
|
|
* you should always send removeObserver: or removeObserver:name:object: to
|
2009-01-13 15:57:38 +00:00
|
|
|
|
* the notification center before releasing these objects.<br />
|
|
|
|
|
* </p>
|
2006-01-10 07:24:54 +00:00
|
|
|
|
*
|
2024-04-30 16:44:26 +00:00
|
|
|
|
* <p>While it is good practice to remove an observer before releasing it,
|
|
|
|
|
* currently on MacOS it is possible to remove an observer even after the
|
|
|
|
|
* object has been deallocated. This is not documented behavior from Apple
|
|
|
|
|
* and could change at any time. In the interests of compatibility, this behavior
|
|
|
|
|
* will also be supported here.
|
|
|
|
|
* </p>
|
|
|
|
|
*
|
2006-01-10 07:24:54 +00:00
|
|
|
|
* <p>NB. For MacOS-X compatibility, adding an observer multiple times will
|
|
|
|
|
* register it to receive multiple copies of any matching notification, however
|
|
|
|
|
* removing an observer will remove <em>all</em> of the multiple registrations.
|
|
|
|
|
* </p>
|
2024-04-30 16:44:26 +00:00
|
|
|
|
*
|
2004-06-22 22:40:40 +00:00
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
- (void) addObserver: (id)observer
|
|
|
|
|
selector: (SEL)selector
|
|
|
|
|
name: (NSString*)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
Observation *list;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
Observation *o;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapTable m;
|
|
|
|
|
GSIMapNode n;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (observer == nil)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Nil observer passed to addObserver ..."];
|
|
|
|
|
|
|
|
|
|
if (selector == 0)
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Null selector passed to addObserver ..."];
|
|
|
|
|
|
2013-05-13 07:09:41 +00:00
|
|
|
|
if ([observer respondsToSelector: selector] == NO)
|
2013-04-08 08:33:12 +00:00
|
|
|
|
{
|
2013-05-13 07:09:41 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"[%@-%@] Observer '%@' does not respond to selector '%@'",
|
|
|
|
|
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
|
|
|
|
|
observer, NSStringFromSelector(selector)];
|
2013-04-08 08:33:12 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
lockNCTable(TABLE);
|
|
|
|
|
|
2013-05-13 07:09:41 +00:00
|
|
|
|
o = obsNew(TABLE, selector, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2024-04-30 16:44:26 +00:00
|
|
|
|
if (object_getClass(observer) == GSNotificationObserverClass)
|
|
|
|
|
{
|
|
|
|
|
[TABLE->retainedObjectsTable addObject:observer];
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* Record the Observation in one of the linked lists.
|
|
|
|
|
*
|
2006-01-10 07:24:54 +00:00
|
|
|
|
* NB. It is possible to register an observer for a notification more than
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* once - in which case, the observer will receive multiple messages when
|
|
|
|
|
* the notification is posted... odd, but the MacOS-X docs specify this.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Locate the map table for this name - create it if not present.
|
|
|
|
|
*/
|
2005-10-07 09:57:51 +00:00
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)(id)name);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
m = mapNew(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* As this is the first observation for the given name, we take a
|
1999-06-17 13:17:28 +00:00
|
|
|
|
* copy of the name so it cannot be mutated while in the map.
|
|
|
|
|
*/
|
|
|
|
|
name = [name copyWithZone: NSDefaultMallocZone()];
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIMapAddPair(NAMED, (GSIMapKey)(id)name, (GSIMapVal)(void*)m);
|
2011-02-12 09:00:18 +00:00
|
|
|
|
GS_CONSUMED(name)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Add the observation to the list for the correct object.
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n == 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
o->next = ENDOBS;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapAddPair(m, (GSIMapKey)object, (GSIMapVal)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
list = (Observation*)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->next = list->next;
|
|
|
|
|
list->next = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (object)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
|
|
|
|
if (n == 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
o->next = ENDOBS;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
GSIMapAddPair(NAMELESS, (GSIMapKey)object, (GSIMapVal)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
list = (Observation*)n->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o->next = list->next;
|
|
|
|
|
list->next = o;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
o->next = WILDCARD;
|
|
|
|
|
WILDCARD = o;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 14:23:09 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Returns a new observer added to the notification center, in order to
|
|
|
|
|
* observe the given notification name posted by an object or any object (if
|
|
|
|
|
* the object argument is nil).</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>For the name and object arguments, the constraints and behavior described
|
2023-10-30 16:56:05 +00:00
|
|
|
|
* in -addObserver:selector:name:object: remain valid.</p>
|
2014-02-14 14:23:09 +00:00
|
|
|
|
*
|
|
|
|
|
* <p>For each notification received by the center, the observer will execute
|
|
|
|
|
* the notification block. If the queue is not nil, the notification block is
|
|
|
|
|
* wrapped in a NSOperation and scheduled in the queue, otherwise the block is
|
|
|
|
|
* executed immediately in the posting thread.</p>
|
|
|
|
|
*/
|
|
|
|
|
- (id) addObserverForName: (NSString *)name
|
|
|
|
|
object: (id)object
|
|
|
|
|
queue: (NSOperationQueue *)queue
|
|
|
|
|
usingBlock: (GSNotificationBlock)block
|
|
|
|
|
{
|
2023-10-30 16:56:05 +00:00
|
|
|
|
GSNotificationObserver *observer =
|
|
|
|
|
[[GSNotificationObserver alloc] initWithQueue: queue block: block];
|
2014-02-14 14:23:09 +00:00
|
|
|
|
|
2023-10-30 16:56:05 +00:00
|
|
|
|
[self addObserver: observer
|
|
|
|
|
selector: @selector(didReceiveNotification:)
|
|
|
|
|
name: name
|
|
|
|
|
object: object];
|
2014-02-14 14:23:09 +00:00
|
|
|
|
|
2024-01-09 08:25:28 +00:00
|
|
|
|
return observer; // Released when observer is removed.
|
2014-02-14 14:23:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Deregisters observer for notifications matching name and/or object. If
|
|
|
|
|
* either or both is nil, they act like wildcards. The observer may still
|
|
|
|
|
* remain registered for other notifications; use -removeObserver: to remove
|
|
|
|
|
* it from all. If observer is nil, the effect is to remove all registrees
|
|
|
|
|
* for the specified notifications, unless both observer and name are nil, in
|
|
|
|
|
* which case nothing is done.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
- (void) removeObserver: (id)observer
|
1999-06-24 19:30:29 +00:00
|
|
|
|
name: (NSString*)name
|
|
|
|
|
object: (id)object
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (name == nil && object == nil && observer == nil)
|
2024-01-09 08:25:28 +00:00
|
|
|
|
{
|
2004-03-16 08:42:35 +00:00
|
|
|
|
return;
|
2024-01-09 08:25:28 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* NB. The removal algorithm depends on an implementation characteristic
|
|
|
|
|
* of our map tables - while enumerating a table, it is safe to remove
|
|
|
|
|
* the entry returned by the enumerator.
|
|
|
|
|
*/
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
lockNCTable(TABLE);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
if (name == nil && object == nil)
|
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
WILDCARD = listPurge(WILDCARD, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (name == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapEnumerator_t e0;
|
|
|
|
|
GSIMapNode n0;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* First try removing all named items set for this object.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
2000-12-21 10:31:33 +00:00
|
|
|
|
e0 = GSIMapEnumeratorForMap(NAMED);
|
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
|
|
|
|
while (n0 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapTable m = (GSIMapTable)n0->value.ptr;
|
|
|
|
|
NSString *thisName = (NSString*)n0->key.obj;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
if (object == nil)
|
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapEnumerator_t e1 = GSIMapEnumeratorForMap(m);
|
|
|
|
|
GSIMapNode n1 = GSIMapEnumeratorNextNode(&e1);
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Nil object and nil name, so we step through all the maps
|
|
|
|
|
* keyed under the current name and remove all the objects
|
|
|
|
|
* that match the observer.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
while (n1 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapNode next = GSIMapEnumeratorNextNode(&e1);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
|
|
|
|
purgeMapNode(m, n1, observer);
|
|
|
|
|
n1 = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapNode n1;
|
|
|
|
|
|
1999-06-17 13:17:28 +00:00
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Nil name, but non-nil object - we locate the map for the
|
|
|
|
|
* specified object, and remove all the items that match
|
|
|
|
|
* the observer.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
n1 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n1 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
purgeMapNode(m, n1, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we removed all the observations keyed under this name, we
|
|
|
|
|
* must remove the map table too.
|
|
|
|
|
*/
|
|
|
|
|
if (m->nodeCount == 0)
|
|
|
|
|
{
|
|
|
|
|
mapFree(TABLE, m);
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIMapRemoveKey(NAMED, (GSIMapKey)(id)thisName);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
1999-06-24 19:30:29 +00:00
|
|
|
|
* Now remove unnamed items
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
e0 = GSIMapEnumeratorForMap(NAMELESS);
|
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
|
|
|
|
while (n0 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapNode next = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2000-12-21 10:31:33 +00:00
|
|
|
|
purgeMapNode(NAMELESS, n0, observer);
|
|
|
|
|
n0 = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n0 = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
|
|
|
|
if (n0 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
purgeMapNode(NAMELESS, n0, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
else
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapTable m;
|
|
|
|
|
GSIMapEnumerator_t e0;
|
|
|
|
|
GSIMapNode n0;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Locate the map table for this name.
|
|
|
|
|
*/
|
2005-10-07 09:57:51 +00:00
|
|
|
|
n0 = GSIMapNodeForKey(NAMED, (GSIMapKey)((id)name));
|
2000-12-21 10:31:33 +00:00
|
|
|
|
if (n0 == 0)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
return; /* Nothing to do. */
|
|
|
|
|
}
|
2000-12-21 10:31:33 +00:00
|
|
|
|
m = (GSIMapTable)n0->value.ptr;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (object == nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
e0 = GSIMapEnumeratorForMap(m);
|
|
|
|
|
n0 = GSIMapEnumeratorNextNode(&e0);
|
|
|
|
|
|
|
|
|
|
while (n0 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
GSIMapNode next = GSIMapEnumeratorNextNode(&e0);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2000-12-21 10:31:33 +00:00
|
|
|
|
purgeMapNode(m, n0, observer);
|
|
|
|
|
n0 = next;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
n0 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n0 != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2000-12-21 10:31:33 +00:00
|
|
|
|
purgeMapNode(m, n0, observer);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (m->nodeCount == 0)
|
|
|
|
|
{
|
|
|
|
|
mapFree(TABLE, m);
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIMapRemoveKey(NAMED, (GSIMapKey)((id)name));
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
2024-01-09 08:25:28 +00:00
|
|
|
|
|
2024-04-30 16:44:26 +00:00
|
|
|
|
if ([TABLE->retainedObjectsTable containsObject:observer])
|
2024-01-09 08:25:28 +00:00
|
|
|
|
{
|
2024-04-30 16:44:26 +00:00
|
|
|
|
[TABLE->retainedObjectsTable removeObject:observer];
|
2024-01-09 08:25:28 +00:00
|
|
|
|
RELEASE(observer);
|
|
|
|
|
}
|
2024-04-30 16:44:26 +00:00
|
|
|
|
|
|
|
|
|
unlockNCTable(TABLE);
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Deregisters observer from all notifications. This should be called before
|
|
|
|
|
* the observer is deallocated.
|
|
|
|
|
*/
|
1999-06-24 19:30:29 +00:00
|
|
|
|
- (void) removeObserver: (id)observer
|
|
|
|
|
{
|
|
|
|
|
if (observer == nil)
|
2004-03-30 14:09:03 +00:00
|
|
|
|
return;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
[self removeObserver: observer name: nil object: nil];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-07 13:29:31 +00:00
|
|
|
|
/**
|
2002-08-09 07:43:47 +00:00
|
|
|
|
* Private method to perform the actual posting of a notification.
|
|
|
|
|
* Release the notification before returning, or before we raise
|
|
|
|
|
* any exception ... to avoid leaks.
|
1999-06-17 13:17:28 +00:00
|
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
|
- (void) _postAndRelease: (NSNotification*)notification
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
|
|
|
|
Observation *o;
|
|
|
|
|
unsigned count;
|
2002-08-09 07:43:47 +00:00
|
|
|
|
NSString *name = [notification name];
|
|
|
|
|
id object;
|
2004-03-31 13:23:38 +00:00
|
|
|
|
GSIMapNode n;
|
|
|
|
|
GSIMapTable m;
|
|
|
|
|
GSIArrayItem i[64];
|
|
|
|
|
GSIArray_t b;
|
|
|
|
|
GSIArray a = &b;
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2002-08-07 13:29:31 +00:00
|
|
|
|
if (name == nil)
|
|
|
|
|
{
|
2002-08-09 07:43:47 +00:00
|
|
|
|
RELEASE(notification);
|
2002-08-07 13:29:31 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Tried to post a notification with no name."];
|
|
|
|
|
}
|
2002-08-14 07:36:25 +00:00
|
|
|
|
object = [notification object];
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|
2004-03-31 13:23:38 +00:00
|
|
|
|
/*
|
2009-01-13 15:57:38 +00:00
|
|
|
|
* Lock the table of observations while we traverse it.
|
|
|
|
|
*
|
|
|
|
|
* The table of observations contains weak pointers which are zeroed when
|
2022-02-17 10:08:18 +00:00
|
|
|
|
* the observers get destroyed. So to avoid consistency problems
|
|
|
|
|
* we use scanned memory in the array in the case where there are more
|
2009-03-10 11:10:27 +00:00
|
|
|
|
* than the 64 observers we allowed room for on the stack.
|
2004-03-31 13:23:38 +00:00
|
|
|
|
*/
|
2009-03-10 11:10:27 +00:00
|
|
|
|
GSIArrayInitWithZoneAndStaticCapacity(a, _zone, 64, i);
|
|
|
|
|
lockNCTable(TABLE);
|
2004-03-31 13:23:38 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find all the observers that specified neither NAME nor OBJECT.
|
|
|
|
|
*/
|
2009-02-11 09:31:30 +00:00
|
|
|
|
for (o = WILDCARD = purgeCollected(WILDCARD); o != ENDOBS; o = o->next)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2004-03-31 13:23:38 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2004-03-31 13:23:38 +00:00
|
|
|
|
/*
|
|
|
|
|
* Find the observers that specified OBJECT, but didn't specify NAME.
|
|
|
|
|
*/
|
|
|
|
|
if (object)
|
|
|
|
|
{
|
|
|
|
|
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
|
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2009-02-11 09:31:30 +00:00
|
|
|
|
o = purgeCollectedFromMapNode(NAMELESS, n);
|
2004-03-31 13:23:38 +00:00
|
|
|
|
while (o != ENDOBS)
|
1999-06-24 19:30:29 +00:00
|
|
|
|
{
|
2004-03-31 13:23:38 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
|
|
|
|
o = o->next;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
2004-03-31 13:23:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2004-03-31 13:23:38 +00:00
|
|
|
|
/*
|
|
|
|
|
* Find the observers of NAME, except those observers with a non-nil OBJECT
|
|
|
|
|
* that doesn't match the notification's OBJECT).
|
|
|
|
|
*/
|
|
|
|
|
if (name)
|
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
n = GSIMapNodeForKey(NAMED, (GSIMapKey)((id)name));
|
2004-03-31 13:23:38 +00:00
|
|
|
|
if (n)
|
|
|
|
|
{
|
|
|
|
|
m = (GSIMapTable)n->value.ptr;
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2004-03-31 13:23:38 +00:00
|
|
|
|
{
|
|
|
|
|
m = 0;
|
|
|
|
|
}
|
|
|
|
|
if (m != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
1999-06-24 19:30:29 +00:00
|
|
|
|
/*
|
2004-03-31 13:23:38 +00:00
|
|
|
|
* First, observers with a matching object.
|
1999-06-24 19:30:29 +00:00
|
|
|
|
*/
|
2004-03-31 13:23:38 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
|
|
|
|
|
if (n != 0)
|
1999-09-13 04:11:39 +00:00
|
|
|
|
{
|
2009-02-11 09:31:30 +00:00
|
|
|
|
o = purgeCollectedFromMapNode(m, n);
|
2004-03-31 13:23:38 +00:00
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-03-31 13:23:38 +00:00
|
|
|
|
if (object != nil)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2004-03-31 13:23:38 +00:00
|
|
|
|
/*
|
|
|
|
|
* Now observers with a nil object.
|
|
|
|
|
*/
|
2021-11-11 15:56:23 +00:00
|
|
|
|
n = GSIMapNodeForSimpleKey(m, (GSIMapKey)(id)nil);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
if (n != 0)
|
1999-06-17 13:17:28 +00:00
|
|
|
|
{
|
2009-02-11 09:31:30 +00:00
|
|
|
|
o = purgeCollectedFromMapNode(m, n);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
while (o != ENDOBS)
|
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIArrayAddItem(a, (GSIArrayItem)o);
|
1999-06-17 13:17:28 +00:00
|
|
|
|
o = o->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
}
|
2004-03-31 13:23:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2016-03-25 11:15:28 +00:00
|
|
|
|
/* Finished with the table ... we can unlock it,
|
2004-03-31 13:23:38 +00:00
|
|
|
|
*/
|
2009-03-10 11:10:27 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
1999-06-24 19:30:29 +00:00
|
|
|
|
|
2004-03-31 13:23:38 +00:00
|
|
|
|
/*
|
|
|
|
|
* Now send all the notifications.
|
|
|
|
|
*/
|
|
|
|
|
count = GSIArrayCount(a);
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
o = GSIArrayItemAtIndex(a, count).ext;
|
|
|
|
|
if (o->next != 0)
|
|
|
|
|
{
|
2007-09-09 07:53:20 +00:00
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
2013-05-13 07:09:41 +00:00
|
|
|
|
[o->observer performSelector: o->selector
|
|
|
|
|
withObject: notification];
|
2007-09-09 07:53:20 +00:00
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
2020-10-29 09:04:51 +00:00
|
|
|
|
BOOL logged;
|
|
|
|
|
|
|
|
|
|
/* Try to report the notification along with the exception,
|
|
|
|
|
* but if there's a problem with the notification itself,
|
|
|
|
|
* we just log the exception.
|
|
|
|
|
*/
|
|
|
|
|
NS_DURING
|
|
|
|
|
NSLog(@"Problem posting %@: %@", notification, localException);
|
|
|
|
|
logged = YES;
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
logged = NO;
|
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
if (NO == logged)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Problem posting notification: %@", localException);
|
|
|
|
|
}
|
2007-09-09 07:53:20 +00:00
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1999-06-17 13:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2004-09-29 08:47:25 +00:00
|
|
|
|
lockNCTable(TABLE);
|
2004-03-31 13:23:38 +00:00
|
|
|
|
GSIArrayEmpty(a);
|
2004-09-29 08:47:25 +00:00
|
|
|
|
unlockNCTable(TABLE);
|
2004-03-31 13:23:38 +00:00
|
|
|
|
|
2002-08-07 13:29:31 +00:00
|
|
|
|
RELEASE(notification);
|
1996-02-13 16:09:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-08-09 07:43:47 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Posts notification to all the observers that match its NAME and OBJECT.<br />
|
|
|
|
|
* The GNUstep implementation calls -postNotificationName:object:userInfo: to
|
|
|
|
|
* perform the actual posting.
|
|
|
|
|
*/
|
|
|
|
|
- (void) postNotification: (NSNotification*)notification
|
|
|
|
|
{
|
|
|
|
|
if (notification == nil)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Tried to post a nil notification."];
|
|
|
|
|
}
|
|
|
|
|
[self _postAndRelease: RETAIN(notification)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates and posts a notification using the
|
|
|
|
|
* -postNotificationName:object:userInfo: passing a nil user info argument.
|
|
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
|
- (void) postNotificationName: (NSString*)name
|
2002-08-09 07:43:47 +00:00
|
|
|
|
object: (id)object
|
|
|
|
|
{
|
|
|
|
|
[self postNotificationName: name object: object userInfo: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The preferred method for posting a notification.
|
|
|
|
|
* <br />
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
|
- (void) postNotificationName: (NSString*)name
|
2002-08-09 07:43:47 +00:00
|
|
|
|
object: (id)object
|
|
|
|
|
userInfo: (NSDictionary*)info
|
|
|
|
|
{
|
|
|
|
|
GSNotification *notification;
|
|
|
|
|
|
|
|
|
|
notification = (id)NSAllocateObject(concrete, 0, NSDefaultMallocZone());
|
2012-01-30 11:31:40 +00:00
|
|
|
|
notification->_name = [name copyWithZone: [self zone]];
|
|
|
|
|
notification->_object = [object retain];
|
|
|
|
|
notification->_info = [info retain];
|
2002-08-09 07:43:47 +00:00
|
|
|
|
[self _postAndRelease: notification];
|
|
|
|
|
}
|
|
|
|
|
|
1996-02-13 16:09:50 +00:00
|
|
|
|
@end
|
1999-06-17 13:17:28 +00:00
|
|
|
|
|