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:
rfm 2008-03-03 12:04:37 +00:00
parent fa3351a70f
commit cadb5df03b
4 changed files with 495 additions and 230 deletions

View file

@ -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

View file

@ -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,

View file

@ -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];
} }

View file

@ -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
{ {
} }