mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 18:21:04 +00:00
Rewrite for MacOS-X compatibility fixes and enhancements plus some bugfixes.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@26181 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
fa3351a70f
commit
cadb5df03b
4 changed files with 495 additions and 230 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
2008-03-03 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Source/NSNull.m: Make result of description method match MacOSX
|
||||||
|
* Headers/Foundation/NSKeyValueObserving.h: Add new MacOSX features.
|
||||||
|
* Source/NSKeyValueObserving.m: Rewrite code for handling observations
|
||||||
|
and notifications. Fix various bugs and add new options from
|
||||||
|
version 10.5 of MacOS-X
|
||||||
|
|
||||||
2008-03-02 Richard Frith-Macdonald <rfm@gnu.org>
|
2008-03-02 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSKeyValueCoding.m: Avoid calling deprecated methods as a
|
* Source/NSKeyValueCoding.m: Avoid calling deprecated methods as a
|
||||||
|
|
|
@ -42,6 +42,10 @@ extern "C" {
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NSKeyValueObservingOptionNew = 1,
|
NSKeyValueObservingOptionNew = 1,
|
||||||
NSKeyValueObservingOptionOld = 2
|
NSKeyValueObservingOptionOld = 2
|
||||||
|
#if OS_API_VERSION(100500,GS_API_LATEST)
|
||||||
|
, NSKeyValueObservingOptionInitial = 4,
|
||||||
|
NSKeyValueObservingOptionPrior = 8
|
||||||
|
#endif
|
||||||
} NSKeyValueObservingOptions;
|
} NSKeyValueObservingOptions;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -62,6 +66,9 @@ GS_EXPORT NSString *const NSKeyValueChangeIndexesKey;
|
||||||
GS_EXPORT NSString *const NSKeyValueChangeKindKey;
|
GS_EXPORT NSString *const NSKeyValueChangeKindKey;
|
||||||
GS_EXPORT NSString *const NSKeyValueChangeNewKey;
|
GS_EXPORT NSString *const NSKeyValueChangeNewKey;
|
||||||
GS_EXPORT NSString *const NSKeyValueChangeOldKey;
|
GS_EXPORT NSString *const NSKeyValueChangeOldKey;
|
||||||
|
#if OS_API_VERSION(100500,GS_API_LATEST)
|
||||||
|
GS_EXPORT NSString *const NSKeyValueChangeNotificationIsPriorKey;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Given that the receiver has been registered as an observer
|
/* Given that the receiver has been registered as an observer
|
||||||
* of the value at a key path relative to an object,
|
* of the value at a key path relative to an object,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||||
|
|
||||||
Written by Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
Written by Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
||||||
Date: 2005
|
Date: 2005-2008
|
||||||
|
|
||||||
This file is part of the GNUstep Base Library.
|
This file is part of the GNUstep Base Library.
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@
|
||||||
#import "Foundation/NSLock.h"
|
#import "Foundation/NSLock.h"
|
||||||
#import "Foundation/NSMapTable.h"
|
#import "Foundation/NSMapTable.h"
|
||||||
#import "Foundation/NSMethodSignature.h"
|
#import "Foundation/NSMethodSignature.h"
|
||||||
|
#import "Foundation/NSNull.h"
|
||||||
#import "Foundation/NSObject.h"
|
#import "Foundation/NSObject.h"
|
||||||
#import "Foundation/NSSet.h"
|
#import "Foundation/NSSet.h"
|
||||||
#import "Foundation/NSString.h"
|
#import "Foundation/NSString.h"
|
||||||
|
@ -63,26 +64,25 @@
|
||||||
* with a another generic setter.
|
* with a another generic setter.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
NSString *const NSKeyValueChangeIndexesKey
|
NSString *const NSKeyValueChangeIndexesKey = @"indexes";
|
||||||
= @"NSKeyValueChangeIndexesKey";
|
NSString *const NSKeyValueChangeKindKey = @"kind";
|
||||||
NSString *const NSKeyValueChangeKindKey
|
NSString *const NSKeyValueChangeNewKey = @"new";
|
||||||
= @"NSKeyValueChangeKindKey";
|
NSString *const NSKeyValueChangeOldKey = @"old";
|
||||||
NSString *const NSKeyValueChangeNewKey
|
NSString *const NSKeyValueChangeNotificationIsPriorKey = @"notificationIsPrior";
|
||||||
= @"NSKeyValueChangeNewKey";
|
|
||||||
NSString *const NSKeyValueChangeOldKey
|
|
||||||
= @"NSKeyValueChangeOldKey";
|
|
||||||
|
|
||||||
static NSRecursiveLock *kvoLock = nil;
|
static NSRecursiveLock *kvoLock = nil;
|
||||||
static NSMapTable *classTable = 0;
|
static NSMapTable *classTable = 0;
|
||||||
static NSMapTable *infoTable = 0;
|
static NSMapTable *infoTable = 0;
|
||||||
static NSMapTable *dependentKeyTable;
|
static NSMapTable *dependentKeyTable;
|
||||||
static Class baseClass;
|
static Class baseClass;
|
||||||
|
static id null;
|
||||||
|
|
||||||
static inline void setup()
|
static inline void setup()
|
||||||
{
|
{
|
||||||
if (kvoLock == nil)
|
if (kvoLock == nil)
|
||||||
{
|
{
|
||||||
kvoLock = [GSLazyRecursiveLock new];
|
kvoLock = [GSLazyRecursiveLock new];
|
||||||
|
null = [[NSNull null] retain];
|
||||||
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||||
NSNonOwnedPointerMapValueCallBacks, 128);
|
NSNonOwnedPointerMapValueCallBacks, 128);
|
||||||
infoTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
infoTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||||
|
@ -132,6 +132,31 @@ static inline void setup()
|
||||||
- (void) setterShort: (unsigned short)val;
|
- (void) setterShort: (unsigned short)val;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/* An instance of this records all the information for a single observation.
|
||||||
|
*/
|
||||||
|
@interface GSKVOObservation : NSObject
|
||||||
|
{
|
||||||
|
@public
|
||||||
|
NSObject *observer; // Not retained
|
||||||
|
void *context;
|
||||||
|
int options;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
/* An instance of thsi records the observations for a key path and the
|
||||||
|
* recursion state of the process of sending notifications.
|
||||||
|
*/
|
||||||
|
@interface GSKVOPathInfo : NSObject
|
||||||
|
{
|
||||||
|
@public
|
||||||
|
unsigned recursion;
|
||||||
|
unsigned allOptions;
|
||||||
|
NSMutableArray *observations;
|
||||||
|
NSMutableDictionary *change;
|
||||||
|
}
|
||||||
|
- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f;
|
||||||
|
@end
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instances of this class are created to hold information about the
|
* Instances of this class are created to hold information about the
|
||||||
* observers monitoring a particular object which is being observed.
|
* observers monitoring a particular object which is being observed.
|
||||||
|
@ -141,14 +166,13 @@ static inline void setup()
|
||||||
NSObject *instance; // Not retained.
|
NSObject *instance; // Not retained.
|
||||||
NSLock *iLock;
|
NSLock *iLock;
|
||||||
NSMapTable *paths;
|
NSMapTable *paths;
|
||||||
NSMutableDictionary *changes;
|
|
||||||
}
|
}
|
||||||
- (NSMutableDictionary *) changeForKey: (NSString *)key;
|
- (GSKVOPathInfo *) lockReturningPathInfoForKey: (NSString *)key;
|
||||||
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath;
|
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath;
|
||||||
- (id) initWithInstance: (NSObject*)i;
|
- (id) initWithInstance: (NSObject*)i;
|
||||||
|
- (NSObject*) instance;
|
||||||
- (BOOL) isUnobserved;
|
- (BOOL) isUnobserved;
|
||||||
- (void) notifyForKey: (NSString *)aKey ofChange: (NSDictionary *)change;
|
- (void) unlock;
|
||||||
- (void) setChange: (NSMutableDictionary *)info forKey: (NSString *)key;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -778,36 +802,202 @@ replacementForClass(Class c)
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation GSKVOObservation
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GSKVOPathInfo
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
[change release];
|
||||||
|
[observations release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) init
|
||||||
|
{
|
||||||
|
change = [NSMutableDictionary new];
|
||||||
|
observations = [NSMutableArray new];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f
|
||||||
|
{
|
||||||
|
unsigned count;
|
||||||
|
id oldValue;
|
||||||
|
id newValue;
|
||||||
|
|
||||||
|
if (f == YES)
|
||||||
|
{
|
||||||
|
if ((allOptions & NSKeyValueObservingOptionPrior) == 0)
|
||||||
|
{
|
||||||
|
return; // Nothing to do.
|
||||||
|
}
|
||||||
|
[change setObject: [NSNumber numberWithBool: YES]
|
||||||
|
forKey: NSKeyValueChangeNotificationIsPriorKey];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[change removeObjectForKey: NSKeyValueChangeNotificationIsPriorKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
oldValue = [[change objectForKey: NSKeyValueChangeOldKey] retain];
|
||||||
|
if (oldValue == nil)
|
||||||
|
{
|
||||||
|
oldValue = null;
|
||||||
|
}
|
||||||
|
newValue = [[change objectForKey: NSKeyValueChangeNewKey] retain];
|
||||||
|
if (newValue == nil)
|
||||||
|
{
|
||||||
|
newValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = [observations count];
|
||||||
|
while (count-- > 0)
|
||||||
|
{
|
||||||
|
GSKVOObservation *o = [observations objectAtIndex: count];
|
||||||
|
|
||||||
|
if (f == YES)
|
||||||
|
{
|
||||||
|
if ((o->options & NSKeyValueObservingOptionPrior) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (o->options & NSKeyValueObservingOptionNew)
|
||||||
|
{
|
||||||
|
[change setObject: newValue
|
||||||
|
forKey: NSKeyValueChangeNewKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o->options & NSKeyValueObservingOptionOld)
|
||||||
|
{
|
||||||
|
[change setObject: oldValue
|
||||||
|
forKey: NSKeyValueChangeOldKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
[o->observer observeValueForKeyPath: aKey
|
||||||
|
ofObject: instance
|
||||||
|
change: change
|
||||||
|
context: o->context];
|
||||||
|
}
|
||||||
|
|
||||||
|
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
|
||||||
|
[oldValue release];
|
||||||
|
[change setObject: newValue forKey: NSKeyValueChangeNewKey];
|
||||||
|
[newValue release];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation GSKVOInfo
|
@implementation GSKVOInfo
|
||||||
|
|
||||||
|
- (NSObject*) instance
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Locks receiver and returns path info on success, otherwise
|
||||||
|
* leaves receiver munlocked and returns nil.
|
||||||
|
*/
|
||||||
|
- (GSKVOPathInfo*) lockReturningPathInfoForKey: (NSString*)key
|
||||||
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
|
|
||||||
|
[iLock lock];
|
||||||
|
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)key);
|
||||||
|
if (pathInfo == nil)
|
||||||
|
{
|
||||||
|
[iLock unlock];
|
||||||
|
}
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) unlock
|
||||||
|
{
|
||||||
|
[iLock unlock];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) addObserver: (NSObject*)anObserver
|
- (void) addObserver: (NSObject*)anObserver
|
||||||
forKeyPath: (NSString*)aPath
|
forKeyPath: (NSString*)aPath
|
||||||
options: (NSKeyValueObservingOptions)options
|
options: (NSKeyValueObservingOptions)options
|
||||||
context: (void*)aContext
|
context: (void*)aContext
|
||||||
{
|
{
|
||||||
NSMapTable *observers;
|
GSKVOPathInfo *pathInfo;
|
||||||
NSMapTable *observer;
|
GSKVOObservation *observation;
|
||||||
|
unsigned count;
|
||||||
|
|
||||||
[iLock lock];
|
if ([anObserver respondsToSelector:
|
||||||
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
|
@selector(observeValueForKeyPath:ofObject:change:context:)] == NO)
|
||||||
if (observers == 0)
|
|
||||||
{
|
{
|
||||||
observers = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
|
return;
|
||||||
NSNonOwnedPointerMapValueCallBacks, 8);
|
}
|
||||||
|
[iLock lock];
|
||||||
|
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
|
||||||
|
if (pathInfo == nil)
|
||||||
|
{
|
||||||
|
pathInfo = [GSKVOPathInfo new];
|
||||||
// use immutable object for map key
|
// use immutable object for map key
|
||||||
aPath = [aPath copy];
|
aPath = [aPath copy];
|
||||||
NSMapInsert(paths, (void*)aPath, (void*)observers);
|
NSMapInsert(paths, (void*)aPath, (void*)pathInfo);
|
||||||
RELEASE(aPath);
|
[pathInfo release];
|
||||||
|
[aPath release];
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* FIXME ... should store an object containing context and options.
|
|
||||||
* For simplicity right now, just store context or a dummy value.
|
|
||||||
*/
|
|
||||||
observer = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
|
|
||||||
NSNonOwnedPointerMapValueCallBacks, 3);
|
|
||||||
NSMapInsert(observer, (void *)@"context", aContext);
|
|
||||||
NSMapInsert(observer, (void *)@"options", (void *)options);
|
|
||||||
|
|
||||||
NSMapInsert(observers, (void*)anObserver, observer);
|
observation = nil;
|
||||||
|
pathInfo->allOptions = 0;
|
||||||
|
count = [pathInfo->observations count];
|
||||||
|
while (count-- > 0)
|
||||||
|
{
|
||||||
|
GSKVOObservation *o;
|
||||||
|
|
||||||
|
o = [pathInfo->observations objectAtIndex: count];
|
||||||
|
if (o->observer == anObserver)
|
||||||
|
{
|
||||||
|
o->observer = anObserver;
|
||||||
|
o->context = aContext;
|
||||||
|
o->options = options;
|
||||||
|
observation = o;
|
||||||
|
}
|
||||||
|
pathInfo->allOptions |= o->options;
|
||||||
|
}
|
||||||
|
if (observation == nil)
|
||||||
|
{
|
||||||
|
observation = [GSKVOObservation new];
|
||||||
|
observation->observer = anObserver;
|
||||||
|
observation->context = aContext;
|
||||||
|
observation->options = options;
|
||||||
|
[pathInfo->observations addObject: observation];
|
||||||
|
[observation release];
|
||||||
|
pathInfo->allOptions |= options;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options & NSKeyValueObservingOptionInitial)
|
||||||
|
{
|
||||||
|
/* If the NSKeyValueObservingOptionInitial option is set,
|
||||||
|
* we must send an immediate notification containing the
|
||||||
|
* existing value in the NSKeyValueChangeNewKey
|
||||||
|
*/
|
||||||
|
[pathInfo->change setObject: [NSNumber numberWithInt: 1]
|
||||||
|
forKey: NSKeyValueChangeKindKey];
|
||||||
|
if (options & NSKeyValueObservingOptionNew)
|
||||||
|
{
|
||||||
|
id value;
|
||||||
|
|
||||||
|
value = [instance valueForKey: aPath];
|
||||||
|
if (value == nil)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
[pathInfo->change setObject: value
|
||||||
|
forKey: NSKeyValueChangeNewKey];
|
||||||
|
}
|
||||||
|
[anObserver observeValueForKeyPath: aPath
|
||||||
|
ofObject: instance
|
||||||
|
change: pathInfo->change
|
||||||
|
context: aContext];
|
||||||
|
}
|
||||||
[iLock unlock];
|
[iLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,66 +1005,18 @@ replacementForClass(Class c)
|
||||||
{
|
{
|
||||||
if (paths != 0) NSFreeMapTable(paths);
|
if (paths != 0) NSFreeMapTable(paths);
|
||||||
RELEASE(iLock);
|
RELEASE(iLock);
|
||||||
RELEASE(changes);
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: This method will provide the observer with both the old and new
|
|
||||||
* values in the change dictionary, regardless of what was asked.
|
|
||||||
*/
|
|
||||||
- (void) notifyForKey: (NSString *)aKey ofChange: (NSDictionary *)change
|
|
||||||
{
|
|
||||||
NSMapTable *observers;
|
|
||||||
|
|
||||||
[iLock lock];
|
|
||||||
observers = (NSMapTable*)NSMapGet(paths, (void*)aKey);
|
|
||||||
if (observers != 0)
|
|
||||||
{
|
|
||||||
NSMapEnumerator enumerator;
|
|
||||||
NSObject *observer;
|
|
||||||
NSMapTable *info;
|
|
||||||
void *context;
|
|
||||||
|
|
||||||
enumerator = NSEnumerateMapTable(observers);
|
|
||||||
while (NSNextMapEnumeratorPair(&enumerator,
|
|
||||||
(void **)(&observer), (void **)&info))
|
|
||||||
{
|
|
||||||
if ([observer respondsToSelector:
|
|
||||||
@selector(observeValueForKeyPath:ofObject:change:context:)])
|
|
||||||
{
|
|
||||||
context = NSMapGet(info, (void*)@"context");
|
|
||||||
[observer observeValueForKeyPath: aKey
|
|
||||||
ofObject: instance
|
|
||||||
change: change
|
|
||||||
context: context];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NSEndMapTableEnumeration(&enumerator);
|
|
||||||
}
|
|
||||||
[iLock unlock];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id) initWithInstance: (NSObject*)i
|
- (id) initWithInstance: (NSObject*)i
|
||||||
{
|
{
|
||||||
instance = i;
|
instance = i;
|
||||||
paths = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
paths = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||||
NSNonOwnedPointerMapValueCallBacks, 8);
|
NSObjectMapValueCallBacks, 8);
|
||||||
iLock = [GSLazyRecursiveLock new];
|
iLock = [GSLazyRecursiveLock new];
|
||||||
changes = [[NSMutableDictionary alloc] init];
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setChange: (NSMutableDictionary *)info forKey: (NSString *)key
|
|
||||||
{
|
|
||||||
[changes setValue: info forKey: key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMutableDictionary *) changeForKey: (NSString *)key
|
|
||||||
{
|
|
||||||
return [changes valueForKey: key];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL) isUnobserved
|
- (BOOL) isUnobserved
|
||||||
{
|
{
|
||||||
BOOL result = NO;
|
BOOL result = NO;
|
||||||
|
@ -889,46 +1031,62 @@ replacementForClass(Class c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* removes the observer and returns the context.
|
* removes the observer
|
||||||
*/
|
*/
|
||||||
- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
|
- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
|
||||||
{
|
{
|
||||||
NSMapTable *observers;
|
GSKVOPathInfo *pathInfo;
|
||||||
NSMapTable *observer;
|
|
||||||
|
|
||||||
[iLock lock];
|
[iLock lock];
|
||||||
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
|
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
|
||||||
if (observers != 0)
|
if (pathInfo != nil)
|
||||||
{
|
{
|
||||||
observer = NSMapGet(observers, (void*)anObserver);
|
unsigned count = [pathInfo->observations count];
|
||||||
|
|
||||||
if (observer != 0)
|
pathInfo->allOptions = 0;
|
||||||
|
while (count-- > 0)
|
||||||
{
|
{
|
||||||
NSMapRemove(observers, (void*)anObserver);
|
GSKVOObservation *o;
|
||||||
if (NSCountMapTable(observers) == 0)
|
|
||||||
|
o = [pathInfo->observations objectAtIndex: count];
|
||||||
|
if (o->observer == anObserver)
|
||||||
|
{
|
||||||
|
[pathInfo->observations removeObjectAtIndex: count];
|
||||||
|
if ([pathInfo->observations count] == 0)
|
||||||
{
|
{
|
||||||
NSMapRemove(paths, (void*)aPath);
|
NSMapRemove(paths, (void*)aPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pathInfo->allOptions |= o->options;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
[iLock unlock];
|
[iLock unlock];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath
|
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath
|
||||||
{
|
{
|
||||||
NSMapTable *observers;
|
GSKVOPathInfo *pathInfo;
|
||||||
NSMapTable *observer;
|
|
||||||
void *context = 0;
|
void *context = 0;
|
||||||
|
|
||||||
[iLock lock];
|
[iLock lock];
|
||||||
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
|
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
|
||||||
if (observers != 0)
|
if (pathInfo != nil)
|
||||||
{
|
{
|
||||||
observer = NSMapGet(observers, (void*)anObserver);
|
unsigned count = [pathInfo->observations count];
|
||||||
|
|
||||||
if (observer != 0)
|
while (count-- > 0)
|
||||||
{
|
{
|
||||||
context = NSMapGet(observer, (void*)@"context");
|
GSKVOObservation *o;
|
||||||
|
|
||||||
|
o = [pathInfo->observations objectAtIndex: count];
|
||||||
|
if (o->observer == anObserver)
|
||||||
|
{
|
||||||
|
context = o->context;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[iLock unlock];
|
[iLock unlock];
|
||||||
|
@ -1080,7 +1238,8 @@ replacementForClass(Class c)
|
||||||
keyForForwarding];
|
keyForForwarding];
|
||||||
if (oldValue)
|
if (oldValue)
|
||||||
{
|
{
|
||||||
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
|
[change setObject: oldValue
|
||||||
|
forKey: NSKeyValueChangeOldKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
observedObjectForForwarding = [observedObjectForUpdate
|
observedObjectForForwarding = [observedObjectForUpdate
|
||||||
|
@ -1202,7 +1361,7 @@ replacementForClass(Class c)
|
||||||
if ([info isUnobserved] == YES)
|
if ([info isUnobserved] == YES)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The instance is no longer bing observed ... so we can
|
* The instance is no longer being observed ... so we can
|
||||||
* turn off key-value-observing for it.
|
* turn off key-value-observing for it.
|
||||||
*/
|
*/
|
||||||
isa = [self class];
|
isa = [self class];
|
||||||
|
@ -1288,7 +1447,7 @@ replacementForClass(Class c)
|
||||||
{
|
{
|
||||||
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
|
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
|
||||||
|
|
||||||
if (keys)
|
if (keys != nil)
|
||||||
{
|
{
|
||||||
NSHashTable dependents = NSMapGet(keys, aKey);
|
NSHashTable dependents = NSMapGet(keys, aKey);
|
||||||
|
|
||||||
|
@ -1311,7 +1470,7 @@ replacementForClass(Class c)
|
||||||
{
|
{
|
||||||
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
|
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
|
||||||
|
|
||||||
if (keys)
|
if (keys != nil)
|
||||||
{
|
{
|
||||||
NSHashTable dependents = NSMapGet(keys, aKey);
|
NSHashTable dependents = NSMapGet(keys, aKey);
|
||||||
|
|
||||||
|
@ -1332,60 +1491,93 @@ replacementForClass(Class c)
|
||||||
|
|
||||||
- (void) willChangeValueForKey: (NSString*)aKey
|
- (void) willChangeValueForKey: (NSString*)aKey
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = (GSKVOInfo *)[self observationInfo];
|
info = (GSKVOInfo *)[self observationInfo];
|
||||||
if (info != nil)
|
if (info == nil)
|
||||||
{
|
{
|
||||||
id old;
|
return;
|
||||||
NSMutableDictionary *change;
|
|
||||||
|
|
||||||
change = [info changeForKey: aKey];
|
|
||||||
if (change == nil)
|
|
||||||
{
|
|
||||||
change = [[NSMutableDictionary alloc] initWithCapacity: 1];
|
|
||||||
[info setChange: change forKey: aKey];
|
|
||||||
RELEASE(change);
|
|
||||||
}
|
}
|
||||||
old = [change objectForKey: NSKeyValueChangeNewKey];
|
|
||||||
if (old == nil)
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
{
|
{
|
||||||
|
if (pathInfo->recursion++ == 0)
|
||||||
|
{
|
||||||
|
id old = [pathInfo->change objectForKey: NSKeyValueChangeNewKey];
|
||||||
|
|
||||||
|
if (old != nil)
|
||||||
|
{
|
||||||
|
/* We have set a value for this key already, so the value
|
||||||
|
* we set must now be the old value and we don't need to
|
||||||
|
* refetch it.
|
||||||
|
*/
|
||||||
|
[pathInfo->change setObject: old
|
||||||
|
forKey: NSKeyValueChangeOldKey];
|
||||||
|
[pathInfo->change removeObjectForKey: NSKeyValueChangeNewKey];
|
||||||
|
}
|
||||||
|
else if (pathInfo->allOptions & NSKeyValueObservingOptionOld)
|
||||||
|
{
|
||||||
|
/* We don't have an old value set, so we must fetch the
|
||||||
|
* existing value because at least one observation wants it.
|
||||||
|
*/
|
||||||
old = [self valueForKey: aKey];
|
old = [self valueForKey: aKey];
|
||||||
if (old == nil)
|
if (old == nil)
|
||||||
{
|
{
|
||||||
[change removeObjectForKey: NSKeyValueChangeOldKey];
|
old = null;
|
||||||
}
|
}
|
||||||
else
|
[pathInfo->change setObject: old
|
||||||
{
|
forKey: NSKeyValueChangeOldKey];
|
||||||
[change setObject: old forKey: NSKeyValueChangeOldKey];
|
|
||||||
}
|
}
|
||||||
|
[pathInfo->change setValue:
|
||||||
|
[NSNumber numberWithInt: NSKeyValueChangeSetting]
|
||||||
|
forKey: NSKeyValueChangeKindKey];
|
||||||
|
|
||||||
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
|
||||||
}
|
}
|
||||||
else
|
[info unlock];
|
||||||
{
|
|
||||||
[change setObject: old forKey: NSKeyValueChangeOldKey];
|
|
||||||
}
|
|
||||||
[change removeObjectForKey: NSKeyValueChangeNewKey];
|
|
||||||
[change removeObjectForKey: NSKeyValueChangeKindKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[self willChangeValueForDependentsOfKey: aKey];
|
[self willChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) didChangeValueForKey: (NSString*)aKey
|
- (void) didChangeValueForKey: (NSString*)aKey
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = (GSKVOInfo *)[self observationInfo];
|
info = (GSKVOInfo *)[self observationInfo];
|
||||||
if (info != nil)
|
if (info == nil)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *change;
|
return;
|
||||||
|
|
||||||
change = (NSMutableDictionary *)[info changeForKey: aKey];
|
|
||||||
[change setValue: [self valueForKey: aKey]
|
|
||||||
forKey: NSKeyValueChangeNewKey];
|
|
||||||
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeSetting]
|
|
||||||
forKey: NSKeyValueChangeKindKey];
|
|
||||||
[info notifyForKey: aKey ofChange: change];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
|
{
|
||||||
|
if (pathInfo->recursion == 1)
|
||||||
|
{
|
||||||
|
id value = [self valueForKey: aKey];
|
||||||
|
|
||||||
|
if (value == nil)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
[pathInfo->change setValue: value
|
||||||
|
forKey: NSKeyValueChangeNewKey];
|
||||||
|
[pathInfo->change setValue:
|
||||||
|
[NSNumber numberWithInt: NSKeyValueChangeSetting]
|
||||||
|
forKey: NSKeyValueChangeKindKey];
|
||||||
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: NO];
|
||||||
|
}
|
||||||
|
if (pathInfo->recursion > 0)
|
||||||
|
{
|
||||||
|
pathInfo->recursion--;
|
||||||
|
}
|
||||||
|
[info unlock];
|
||||||
|
}
|
||||||
|
|
||||||
[self didChangeValueForDependentsOfKey: aKey];
|
[self didChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1393,30 +1585,43 @@ replacementForClass(Class c)
|
||||||
valuesAtIndexes: (NSIndexSet*)indexes
|
valuesAtIndexes: (NSIndexSet*)indexes
|
||||||
forKey: (NSString*)aKey
|
forKey: (NSString*)aKey
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = [self observationInfo];
|
info = [self observationInfo];
|
||||||
if (info != nil)
|
if (info == nil)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
|
{
|
||||||
|
if (pathInfo->recursion == 1)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *change;
|
|
||||||
NSMutableArray *array;
|
NSMutableArray *array;
|
||||||
|
|
||||||
change = (NSMutableDictionary *)[info changeForKey: aKey];
|
|
||||||
array = [self valueForKey: aKey];
|
array = [self valueForKey: aKey];
|
||||||
|
[pathInfo->change setValue: [NSNumber numberWithInt: changeKind]
|
||||||
[change setValue: [NSNumber numberWithInt: changeKind] forKey:
|
forKey: NSKeyValueChangeKindKey];
|
||||||
NSKeyValueChangeKindKey];
|
[pathInfo->change setValue: indexes
|
||||||
[change setValue: indexes forKey: NSKeyValueChangeIndexesKey];
|
forKey: NSKeyValueChangeIndexesKey];
|
||||||
|
|
||||||
if (changeKind == NSKeyValueChangeInsertion
|
if (changeKind == NSKeyValueChangeInsertion
|
||||||
|| changeKind == NSKeyValueChangeReplacement)
|
|| changeKind == NSKeyValueChangeReplacement)
|
||||||
{
|
{
|
||||||
[change setValue: [array objectsAtIndexes: indexes]
|
[pathInfo->change setValue: [array objectsAtIndexes: indexes]
|
||||||
forKey: NSKeyValueChangeNewKey];
|
forKey: NSKeyValueChangeNewKey];
|
||||||
}
|
}
|
||||||
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: NO];
|
||||||
[info notifyForKey: aKey ofChange: change];
|
|
||||||
}
|
}
|
||||||
|
if (pathInfo->recursion > 0)
|
||||||
|
{
|
||||||
|
pathInfo->recursion--;
|
||||||
|
}
|
||||||
|
[info unlock];
|
||||||
|
}
|
||||||
|
|
||||||
[self didChangeValueForDependentsOfKey: aKey];
|
[self didChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1424,26 +1629,36 @@ replacementForClass(Class c)
|
||||||
valuesAtIndexes: (NSIndexSet*)indexes
|
valuesAtIndexes: (NSIndexSet*)indexes
|
||||||
forKey: (NSString*)aKey
|
forKey: (NSString*)aKey
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = [self observationInfo];
|
info = [self observationInfo];
|
||||||
|
if (info == nil)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
|
{
|
||||||
|
if (pathInfo->recursion++ == 0)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *change;
|
|
||||||
NSMutableArray *array;
|
NSMutableArray *array;
|
||||||
|
|
||||||
change = [[NSMutableDictionary alloc] initWithCapacity: 1];
|
|
||||||
array = [self valueForKey: aKey];
|
array = [self valueForKey: aKey];
|
||||||
|
|
||||||
if (changeKind == NSKeyValueChangeRemoval
|
if (changeKind == NSKeyValueChangeRemoval
|
||||||
|| changeKind == NSKeyValueChangeReplacement)
|
|| changeKind == NSKeyValueChangeReplacement)
|
||||||
{
|
{
|
||||||
[change setValue: [array objectsAtIndexes: indexes]
|
[pathInfo->change setValue: [array objectsAtIndexes: indexes]
|
||||||
forKey: NSKeyValueChangeOldKey];
|
forKey: NSKeyValueChangeOldKey];
|
||||||
}
|
}
|
||||||
|
[pathInfo->change setValue: [NSNumber numberWithInt: changeKind]
|
||||||
[info setChange: change forKey: aKey];
|
forKey: NSKeyValueChangeKindKey];
|
||||||
RELEASE(change);
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
|
||||||
}
|
}
|
||||||
|
[info unlock];
|
||||||
|
}
|
||||||
|
|
||||||
[self willChangeValueForDependentsOfKey: aKey];
|
[self willChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,21 +1666,29 @@ replacementForClass(Class c)
|
||||||
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
|
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
|
||||||
usingObjects: (NSSet*)objects
|
usingObjects: (NSSet*)objects
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = [self observationInfo];
|
info = [self observationInfo];
|
||||||
if (info != nil)
|
if (info == nil)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
|
{
|
||||||
|
if (pathInfo->recursion++ == 0)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *change;
|
|
||||||
NSMutableSet *set;
|
NSMutableSet *set;
|
||||||
|
|
||||||
change = [[NSMutableDictionary alloc] initWithCapacity: 1];
|
|
||||||
set = [self valueForKey: aKey];
|
set = [self valueForKey: aKey];
|
||||||
|
[pathInfo->change setValue: [set mutableCopy] forKey: @"oldSet"];
|
||||||
[change setValue: [set mutableCopy] forKey: @"oldSet"];
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
|
||||||
[info setChange: change forKey: aKey];
|
|
||||||
RELEASE(change);
|
|
||||||
}
|
}
|
||||||
|
[info unlock];
|
||||||
|
}
|
||||||
|
|
||||||
[self willChangeValueForDependentsOfKey: aKey];
|
[self willChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1473,34 +1696,46 @@ replacementForClass(Class c)
|
||||||
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
|
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
|
||||||
usingObjects: (NSSet*)objects
|
usingObjects: (NSSet*)objects
|
||||||
{
|
{
|
||||||
|
GSKVOPathInfo *pathInfo;
|
||||||
GSKVOInfo *info;
|
GSKVOInfo *info;
|
||||||
|
|
||||||
info = (GSKVOInfo *)[self observationInfo];
|
info = [self observationInfo];
|
||||||
if (info != nil)
|
if (info == nil)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathInfo = [info lockReturningPathInfoForKey: aKey];
|
||||||
|
if (pathInfo != nil)
|
||||||
|
{
|
||||||
|
if (pathInfo->recursion == 1)
|
||||||
{
|
{
|
||||||
NSMutableDictionary *change;
|
|
||||||
NSMutableSet *oldSet;
|
NSMutableSet *oldSet;
|
||||||
NSMutableSet *set;
|
NSMutableSet *set;
|
||||||
|
|
||||||
change = (NSMutableDictionary *)[info changeForKey: aKey];
|
oldSet = [pathInfo->change valueForKey: @"oldSet"];
|
||||||
oldSet = [change valueForKey: @"oldSet"];
|
|
||||||
set = [self valueForKey: aKey];
|
set = [self valueForKey: aKey];
|
||||||
[change setValue: nil forKey: @"oldSet"];
|
[pathInfo->change removeObjectForKey: @"oldSet"];
|
||||||
|
|
||||||
if (mutationKind == NSKeyValueUnionSetMutation)
|
if (mutationKind == NSKeyValueUnionSetMutation)
|
||||||
{
|
{
|
||||||
set = [set mutableCopy];
|
set = [set mutableCopy];
|
||||||
[set minusSet: oldSet];
|
[set minusSet: oldSet];
|
||||||
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeInsertion]
|
[pathInfo->change setValue:
|
||||||
|
[NSNumber numberWithInt: NSKeyValueChangeInsertion]
|
||||||
forKey: NSKeyValueChangeKindKey];
|
forKey: NSKeyValueChangeKindKey];
|
||||||
[change setValue: set forKey: NSKeyValueChangeNewKey];
|
[pathInfo->change setValue: set
|
||||||
|
forKey: NSKeyValueChangeNewKey];
|
||||||
}
|
}
|
||||||
else if (mutationKind == NSKeyValueMinusSetMutation
|
else if (mutationKind == NSKeyValueMinusSetMutation
|
||||||
|| mutationKind == NSKeyValueIntersectSetMutation)
|
|| mutationKind == NSKeyValueIntersectSetMutation)
|
||||||
{
|
{
|
||||||
[oldSet minusSet: set];
|
[oldSet minusSet: set];
|
||||||
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeRemoval]
|
[pathInfo->change setValue:
|
||||||
|
[NSNumber numberWithInt: NSKeyValueChangeRemoval]
|
||||||
forKey: NSKeyValueChangeKindKey];
|
forKey: NSKeyValueChangeKindKey];
|
||||||
[change setValue: oldSet forKey: NSKeyValueChangeOldKey];
|
[pathInfo->change setValue: oldSet
|
||||||
|
forKey: NSKeyValueChangeOldKey];
|
||||||
}
|
}
|
||||||
else if (mutationKind == NSKeyValueSetSetMutation)
|
else if (mutationKind == NSKeyValueSetSetMutation)
|
||||||
{
|
{
|
||||||
|
@ -1511,13 +1746,22 @@ replacementForClass(Class c)
|
||||||
[old minusSet: set];
|
[old minusSet: set];
|
||||||
new = [set mutableCopy];
|
new = [set mutableCopy];
|
||||||
[new minusSet: oldSet];
|
[new minusSet: oldSet];
|
||||||
[change setValue:
|
[pathInfo->change setValue:
|
||||||
[NSNumber numberWithInt: NSKeyValueChangeReplacement]
|
[NSNumber numberWithInt: NSKeyValueChangeReplacement]
|
||||||
forKey: NSKeyValueChangeKindKey];
|
forKey: NSKeyValueChangeKindKey];
|
||||||
[change setValue: old forKey: NSKeyValueChangeOldKey];
|
[pathInfo->change setValue: old
|
||||||
[change setValue: new forKey: NSKeyValueChangeNewKey];
|
forKey: NSKeyValueChangeOldKey];
|
||||||
|
[pathInfo->change setValue: new
|
||||||
|
forKey: NSKeyValueChangeNewKey];
|
||||||
}
|
}
|
||||||
[info notifyForKey: aKey ofChange: change];
|
|
||||||
|
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: NO];
|
||||||
|
}
|
||||||
|
if (pathInfo->recursion > 0)
|
||||||
|
{
|
||||||
|
pathInfo->recursion--;
|
||||||
|
}
|
||||||
|
[info unlock];
|
||||||
}
|
}
|
||||||
[self didChangeValueForDependentsOfKey: aKey];
|
[self didChangeValueForDependentsOfKey: aKey];
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Foundation/NSNull.h"
|
#include "Foundation/NSNull.h"
|
||||||
|
#include "Foundation/NSString.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object to use as a placeholder - in collections for instance.
|
* An object to use as a placeholder - in collections for instance.
|
||||||
|
@ -81,6 +82,11 @@ static NSNull *null = 0;
|
||||||
GSNOSUPERDEALLOC;
|
GSNOSUPERDEALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSString*) description
|
||||||
|
{
|
||||||
|
return @"<null>";
|
||||||
|
}
|
||||||
|
|
||||||
- (void) encodeWithCoder: (NSCoder*)aCoder
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue