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:
Richard Frith-MacDonald 2008-03-03 12:04:37 +00:00
parent 55e5eabfc9
commit f5f2219ee7
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>
* Source/NSKeyValueCoding.m: Avoid calling deprecated methods as a

View file

@ -42,6 +42,10 @@ extern "C" {
typedef enum {
NSKeyValueObservingOptionNew = 1,
NSKeyValueObservingOptionOld = 2
#if OS_API_VERSION(100500,GS_API_LATEST)
, NSKeyValueObservingOptionInitial = 4,
NSKeyValueObservingOptionPrior = 8
#endif
} NSKeyValueObservingOptions;
typedef enum {
@ -62,6 +66,9 @@ GS_EXPORT NSString *const NSKeyValueChangeIndexesKey;
GS_EXPORT NSString *const NSKeyValueChangeKindKey;
GS_EXPORT NSString *const NSKeyValueChangeNewKey;
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
* of the value at a key path relative to an object,

View file

@ -2,7 +2,7 @@
Copyright (C) 2005 Free Software Foundation, Inc.
Written by Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: 2005
Date: 2005-2008
This file is part of the GNUstep Base Library.
@ -35,6 +35,7 @@
#import "Foundation/NSLock.h"
#import "Foundation/NSMapTable.h"
#import "Foundation/NSMethodSignature.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSObject.h"
#import "Foundation/NSSet.h"
#import "Foundation/NSString.h"
@ -63,26 +64,25 @@
* with a another generic setter.
*/
NSString *const NSKeyValueChangeIndexesKey
= @"NSKeyValueChangeIndexesKey";
NSString *const NSKeyValueChangeKindKey
= @"NSKeyValueChangeKindKey";
NSString *const NSKeyValueChangeNewKey
= @"NSKeyValueChangeNewKey";
NSString *const NSKeyValueChangeOldKey
= @"NSKeyValueChangeOldKey";
NSString *const NSKeyValueChangeIndexesKey = @"indexes";
NSString *const NSKeyValueChangeKindKey = @"kind";
NSString *const NSKeyValueChangeNewKey = @"new";
NSString *const NSKeyValueChangeOldKey = @"old";
NSString *const NSKeyValueChangeNotificationIsPriorKey = @"notificationIsPrior";
static NSRecursiveLock *kvoLock = nil;
static NSMapTable *classTable = 0;
static NSMapTable *infoTable = 0;
static NSMapTable *dependentKeyTable;
static Class baseClass;
static id null;
static inline void setup()
{
if (kvoLock == nil)
{
kvoLock = [GSLazyRecursiveLock new];
null = [[NSNull null] retain];
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 128);
infoTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
@ -132,6 +132,31 @@ static inline void setup()
- (void) setterShort: (unsigned short)val;
@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
* observers monitoring a particular object which is being observed.
@ -141,14 +166,13 @@ static inline void setup()
NSObject *instance; // Not retained.
NSLock *iLock;
NSMapTable *paths;
NSMutableDictionary *changes;
}
- (NSMutableDictionary *) changeForKey: (NSString *)key;
- (GSKVOPathInfo *) lockReturningPathInfoForKey: (NSString *)key;
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath;
- (id) initWithInstance: (NSObject*)i;
- (NSObject*) instance;
- (BOOL) isUnobserved;
- (void) notifyForKey: (NSString *)aKey ofChange: (NSDictionary *)change;
- (void) setChange: (NSMutableDictionary *)info forKey: (NSString *)key;
- (void) unlock;
@end
@ -778,36 +802,202 @@ replacementForClass(Class c)
@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
- (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
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
NSMapTable *observers;
NSMapTable *observer;
GSKVOPathInfo *pathInfo;
GSKVOObservation *observation;
unsigned count;
[iLock lock];
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
if (observers == 0)
if ([anObserver respondsToSelector:
@selector(observeValueForKeyPath:ofObject:change:context:)] == NO)
{
observers = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 8);
return;
}
[iLock lock];
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
if (pathInfo == nil)
{
pathInfo = [GSKVOPathInfo new];
// use immutable object for map key
aPath = [aPath copy];
NSMapInsert(paths, (void*)aPath, (void*)observers);
RELEASE(aPath);
NSMapInsert(paths, (void*)aPath, (void*)pathInfo);
[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];
}
@ -815,66 +1005,18 @@ replacementForClass(Class c)
{
if (paths != 0) NSFreeMapTable(paths);
RELEASE(iLock);
RELEASE(changes);
[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
{
instance = i;
paths = NSCreateMapTable(NSObjectMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 8);
NSObjectMapValueCallBacks, 8);
iLock = [GSLazyRecursiveLock new];
changes = [[NSMutableDictionary alloc] init];
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 result = NO;
@ -889,26 +1031,36 @@ replacementForClass(Class c)
}
/*
* removes the observer and returns the context.
* removes the observer
*/
- (void) removeObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath
{
NSMapTable *observers;
NSMapTable *observer;
GSKVOPathInfo *pathInfo;
[iLock lock];
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
if (observers != 0)
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
if (pathInfo != nil)
{
observer = NSMapGet(observers, (void*)anObserver);
unsigned count = [pathInfo->observations count];
if (observer != 0)
{
NSMapRemove(observers, (void*)anObserver);
if (NSCountMapTable(observers) == 0)
{
NSMapRemove(paths, (void*)aPath);
}
pathInfo->allOptions = 0;
while (count-- > 0)
{
GSKVOObservation *o;
o = [pathInfo->observations objectAtIndex: count];
if (o->observer == anObserver)
{
[pathInfo->observations removeObjectAtIndex: count];
if ([pathInfo->observations count] == 0)
{
NSMapRemove(paths, (void*)aPath);
}
}
else
{
pathInfo->allOptions |= o->options;
}
}
}
[iLock unlock];
@ -916,19 +1068,25 @@ replacementForClass(Class c)
- (void*) contextForObserver: (NSObject*)anObserver ofKeyPath: (NSString*)aPath
{
NSMapTable *observers;
NSMapTable *observer;
GSKVOPathInfo *pathInfo;
void *context = 0;
[iLock lock];
observers = (NSMapTable*)NSMapGet(paths, (void*)aPath);
if (observers != 0)
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
if (pathInfo != nil)
{
observer = NSMapGet(observers, (void*)anObserver);
unsigned count = [pathInfo->observations count];
if (observer != 0)
{
context = NSMapGet(observer, (void*)@"context");
while (count-- > 0)
{
GSKVOObservation *o;
o = [pathInfo->observations objectAtIndex: count];
if (o->observer == anObserver)
{
context = o->context;
break;
}
}
}
[iLock unlock];
@ -1080,7 +1238,8 @@ replacementForClass(Class c)
keyForForwarding];
if (oldValue)
{
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
[change setObject: oldValue
forKey: NSKeyValueChangeOldKey];
}
}
observedObjectForForwarding = [observedObjectForUpdate
@ -1202,7 +1361,7 @@ replacementForClass(Class c)
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.
*/
isa = [self class];
@ -1288,7 +1447,7 @@ replacementForClass(Class c)
{
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
if (keys)
if (keys != nil)
{
NSHashTable dependents = NSMapGet(keys, aKey);
@ -1311,7 +1470,7 @@ replacementForClass(Class c)
{
NSMapTable keys = NSMapGet(dependentKeyTable, [self class]);
if (keys)
if (keys != nil)
{
NSHashTable dependents = NSMapGet(keys, aKey);
@ -1332,60 +1491,93 @@ replacementForClass(Class c)
- (void) willChangeValueForKey: (NSString*)aKey
{
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info != nil)
if (info == nil)
{
id old;
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)
{
old = [self valueForKey: aKey];
if (old == nil)
{
[change removeObjectForKey: NSKeyValueChangeOldKey];
}
else
{
[change setObject: old forKey: NSKeyValueChangeOldKey];
}
}
else
{
[change setObject: old forKey: NSKeyValueChangeOldKey];
}
[change removeObjectForKey: NSKeyValueChangeNewKey];
[change removeObjectForKey: NSKeyValueChangeKindKey];
return;
}
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];
if (old == nil)
{
old = null;
}
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
}
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeSetting]
forKey: NSKeyValueChangeKindKey];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
[self willChangeValueForDependentsOfKey: aKey];
}
- (void) didChangeValueForKey: (NSString*)aKey
{
GSKVOInfo *info;
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info != nil)
if (info == nil)
{
NSMutableDictionary *change;
change = (NSMutableDictionary *)[info changeForKey: aKey];
[change setValue: [self valueForKey: aKey]
forKey: NSKeyValueChangeNewKey];
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeSetting]
forKey: NSKeyValueChangeKindKey];
[info notifyForKey: aKey ofChange: change];
return;
}
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];
}
@ -1393,30 +1585,43 @@ replacementForClass(Class c)
valuesAtIndexes: (NSIndexSet*)indexes
forKey: (NSString*)aKey
{
GSKVOInfo *info;
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = [self observationInfo];
if (info != nil)
if (info == nil)
{
NSMutableDictionary *change;
NSMutableArray *array;
change = (NSMutableDictionary *)[info changeForKey: aKey];
array = [self valueForKey: aKey];
[change setValue: [NSNumber numberWithInt: changeKind] forKey:
NSKeyValueChangeKindKey];
[change setValue: indexes forKey: NSKeyValueChangeIndexesKey];
if (changeKind == NSKeyValueChangeInsertion
|| changeKind == NSKeyValueChangeReplacement)
{
[change setValue: [array objectsAtIndexes: indexes]
forKey: NSKeyValueChangeNewKey];
}
[info notifyForKey: aKey ofChange: change];
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion == 1)
{
NSMutableArray *array;
array = [self valueForKey: aKey];
[pathInfo->change setValue: [NSNumber numberWithInt: changeKind]
forKey: NSKeyValueChangeKindKey];
[pathInfo->change setValue: indexes
forKey: NSKeyValueChangeIndexesKey];
if (changeKind == NSKeyValueChangeInsertion
|| changeKind == NSKeyValueChangeReplacement)
{
[pathInfo->change setValue: [array objectsAtIndexes: indexes]
forKey: NSKeyValueChangeNewKey];
}
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: NO];
}
if (pathInfo->recursion > 0)
{
pathInfo->recursion--;
}
[info unlock];
}
[self didChangeValueForDependentsOfKey: aKey];
}
@ -1424,26 +1629,36 @@ replacementForClass(Class c)
valuesAtIndexes: (NSIndexSet*)indexes
forKey: (NSString*)aKey
{
GSKVOInfo *info;
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = [self observationInfo];
if (info == nil)
{
NSMutableDictionary *change;
NSMutableArray *array;
change = [[NSMutableDictionary alloc] initWithCapacity: 1];
array = [self valueForKey: aKey];
if (changeKind == NSKeyValueChangeRemoval
|| changeKind == NSKeyValueChangeReplacement)
{
[change setValue: [array objectsAtIndexes: indexes]
forKey: NSKeyValueChangeOldKey];
}
[info setChange: change forKey: aKey];
RELEASE(change);
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion++ == 0)
{
NSMutableArray *array;
array = [self valueForKey: aKey];
if (changeKind == NSKeyValueChangeRemoval
|| changeKind == NSKeyValueChangeReplacement)
{
[pathInfo->change setValue: [array objectsAtIndexes: indexes]
forKey: NSKeyValueChangeOldKey];
}
[pathInfo->change setValue: [NSNumber numberWithInt: changeKind]
forKey: NSKeyValueChangeKindKey];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
[self willChangeValueForDependentsOfKey: aKey];
}
@ -1451,21 +1666,29 @@ replacementForClass(Class c)
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
usingObjects: (NSSet*)objects
{
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = [self observationInfo];
if (info != nil)
if (info == nil)
{
NSMutableDictionary *change;
NSMutableSet *set;
change = [[NSMutableDictionary alloc] initWithCapacity: 1];
set = [self valueForKey: aKey];
[change setValue: [set mutableCopy] forKey: @"oldSet"];
[info setChange: change forKey: aKey];
RELEASE(change);
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion++ == 0)
{
NSMutableSet *set;
set = [self valueForKey: aKey];
[pathInfo->change setValue: [set mutableCopy] forKey: @"oldSet"];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
[self willChangeValueForDependentsOfKey: aKey];
}
@ -1473,51 +1696,72 @@ replacementForClass(Class c)
withSetMutation: (NSKeyValueSetMutationKind)mutationKind
usingObjects: (NSSet*)objects
{
GSKVOInfo *info;
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info != nil)
info = [self observationInfo];
if (info == nil)
{
NSMutableDictionary *change;
NSMutableSet *oldSet;
NSMutableSet *set;
return;
}
change = (NSMutableDictionary *)[info changeForKey: aKey];
oldSet = [change valueForKey: @"oldSet"];
set = [self valueForKey: aKey];
[change setValue: nil forKey: @"oldSet"];
if (mutationKind == NSKeyValueUnionSetMutation)
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion == 1)
{
set = [set mutableCopy];
[set minusSet: oldSet];
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeInsertion]
forKey: NSKeyValueChangeKindKey];
[change setValue: set forKey: NSKeyValueChangeNewKey];
}
else if (mutationKind == NSKeyValueMinusSetMutation
|| mutationKind == NSKeyValueIntersectSetMutation)
{
[oldSet minusSet: set];
[change setValue: [NSNumber numberWithInt: NSKeyValueChangeRemoval]
forKey: NSKeyValueChangeKindKey];
[change setValue: oldSet forKey: NSKeyValueChangeOldKey];
}
else if (mutationKind == NSKeyValueSetSetMutation)
{
NSMutableSet *old;
NSMutableSet *new;
NSMutableSet *oldSet;
NSMutableSet *set;
old = [oldSet mutableCopy];
[old minusSet: set];
new = [set mutableCopy];
[new minusSet: oldSet];
[change setValue:
[NSNumber numberWithInt: NSKeyValueChangeReplacement]
forKey: NSKeyValueChangeKindKey];
[change setValue: old forKey: NSKeyValueChangeOldKey];
[change setValue: new forKey: NSKeyValueChangeNewKey];
oldSet = [pathInfo->change valueForKey: @"oldSet"];
set = [self valueForKey: aKey];
[pathInfo->change removeObjectForKey: @"oldSet"];
if (mutationKind == NSKeyValueUnionSetMutation)
{
set = [set mutableCopy];
[set minusSet: oldSet];
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeInsertion]
forKey: NSKeyValueChangeKindKey];
[pathInfo->change setValue: set
forKey: NSKeyValueChangeNewKey];
}
else if (mutationKind == NSKeyValueMinusSetMutation
|| mutationKind == NSKeyValueIntersectSetMutation)
{
[oldSet minusSet: set];
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeRemoval]
forKey: NSKeyValueChangeKindKey];
[pathInfo->change setValue: oldSet
forKey: NSKeyValueChangeOldKey];
}
else if (mutationKind == NSKeyValueSetSetMutation)
{
NSMutableSet *old;
NSMutableSet *new;
old = [oldSet mutableCopy];
[old minusSet: set];
new = [set mutableCopy];
[new minusSet: oldSet];
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeReplacement]
forKey: NSKeyValueChangeKindKey];
[pathInfo->change setValue: old
forKey: NSKeyValueChangeOldKey];
[pathInfo->change setValue: new
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];
}

View file

@ -26,6 +26,7 @@
*/
#include "Foundation/NSNull.h"
#include "Foundation/NSString.h"
/**
* An object to use as a placeholder - in collections for instance.
@ -81,6 +82,11 @@ static NSNull *null = 0;
GSNOSUPERDEALLOC;
}
- (NSString*) description
{
return @"<null>";
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
}