Skip to content

Commit

Permalink
Merge pull request #330 from gnustep/NSKeyValueObserving_issue327
Browse files Browse the repository at this point in the history
  • Loading branch information
gcasa authored Oct 8, 2023
2 parents 0d9e456 + 19cfd59 commit e58b83c
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Tests/base/*/GNUmakefile
*.log
*.sum

# Unit test byproducts
*.err
*.out

# Autoconf
autom4te.cache

Expand Down
9 changes: 9 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2023-10-07 Gregory John Casamento <[email protected]>

* Headers/Foundation/NSKeyValueObserving.h
* Source/NSKeyValueObserving.m: Add nethods
removeObserver:forKeyPath:context: and
removeObserver:forObjectsAtIndexes:forKeyPath:context:
* Tests/base/KVC/mutable.m: Add tests for the above
methods.

2023-08-18 Richard Frith-Macdonald <[email protected]>

* Headers/Foundation/NSCache.h: Add _lock ivar
Expand Down
6 changes: 6 additions & 0 deletions Headers/Foundation/NSKeyValueObserving.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ GS_EXPORT NSString *const NSKeyValueChangeNotificationIsPriorKey;
fromObjectsAtIndexes: (NSIndexSet*)indexes
forKeyPath: (NSString*)aPath;

#if OS_API_VERSION(MAC_OS_X_VERSION_10_7,GS_API_LATEST)
- (void) removeObserver: (NSObject*)anObserver
fromObjectsAtIndexes: (NSIndexSet *)indexes
forKeyPath: (NSString*)aPath
context: (void *)context;
#endif
@end

/**
Expand Down
19 changes: 19 additions & 0 deletions Source/NSKeyValueObserving.m
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,25 @@ - (void) removeObserver: (NSObject*)anObserver
}
}

- (void) removeObserver: (NSObject*)anObserver
fromObjectsAtIndexes: (NSIndexSet *)indexes
forKeyPath: (NSString*)aPath
context: (void *)context
{
NSUInteger i = [indexes firstIndex];

while (i != NSNotFound)
{
NSObject *elem = [self objectAtIndex: i];

[elem removeObserver: anObserver
forKeyPath: aPath
context: context];

i = [indexes indexGreaterThanIndex: i];
}
}

@end

/**
Expand Down
95 changes: 93 additions & 2 deletions Tests/base/KVC/mutable.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,56 @@
} aStruct;

@interface Observer : NSObject
{
NSMutableSet *_keysChanged;
}

- (void) reset;

- (NSSet *) keysChanged;

- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context;
@end

@implementation Observer
- (instancetype) init
{
self = [super init];
if (self)
{
_keysChanged = [[NSMutableSet alloc] init];
}
return self;
}

- (void) dealloc
{
RELEASE(_keysChanged);
[super dealloc];
}

- (void) reset;
{
[_keysChanged removeAllObjects];
}

- (NSSet *) keysChanged
{
return _keysChanged;
}

- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
NSLog(@"observeValueForKeyPath: %@\nofObject:%@\nchange:%@\ncontext:%p",
keyPath, object, change, context);

[_keysChanged addObject: keyPath];
}
@end

Expand All @@ -31,6 +67,7 @@ @interface Lists : NSObject
NSMutableArray * numbers;
NSMutableArray * third;
NSString *string;
NSString *string2;
aStruct x;
}

Expand Down Expand Up @@ -115,6 +152,16 @@ - (void) setX: (aStruct)s
x = s;
}

- (void) setString2: (NSString *)s
{
string2 = s;
}

- (NSString *) string2
{
return string2;
}

- (void) willChangeValueForKey: (NSString*)k
{
[super willChangeValueForKey: k];
Expand Down Expand Up @@ -179,11 +226,17 @@ int main(void)
NSMutableArray * proxy;
NSDictionary * temp;

[observer reset];
[list addObserver: observer forKeyPath: @"numbers" options: 15 context: 0];
[list addObserver: observer forKeyPath: @"string" options: 15 context: 0];
[list addObserver: observer forKeyPath: @"string2" options: 15 context: 0];
[list addObserver: observer forKeyPath: @"x" options: 15 context: 0];

[list setValue: @"x" forKey: @"string"];
[list setString2: @"Hello"];

PASS([[observer keysChanged] containsObject: @"string2"],
"string2 did change properly");

proxy = [list mutableArrayValueForKey:@"numbers"];
PASS([proxy isKindOfClass:[NSMutableArray class]],
Expand Down Expand Up @@ -277,8 +330,46 @@ int main(void)

[list removeObserver: observer forKeyPath: @"numbers"];
[list removeObserver: observer forKeyPath: @"string"];
[list removeObserver: observer forKeyPath: @"x"];

[list removeObserver: observer forKeyPath: @"x"];
[list removeObserver: observer forKeyPath: @"string2" context: 0];

// Test if we see the change on string2 after the observer is removed with
// the removeObjserver:forKeyPath:context: method.
[observer reset];
[list setString2: @"Test"];
PASS([[observer keysChanged] containsObject: @"string2"] == NO,
"string2 should NOT have been observed");

// Create an array and add an object, add an observer to that object... test
// it and then remove it and verify the remove.
Lists *obj = [[[Lists alloc] init] autorelease];
NSArray *array = [NSArray arrayWithObject: obj];
NSIndexSet *idxs = [NSIndexSet indexSetWithIndex: 0];

// Add the observer then remove it...
[observer reset];
[array addObserver: observer
toObjectsAtIndexes: idxs
forKeyPath: @"string2"
options: 15
context: 0];

[obj setString2: @"Hello again"];
PASS([[observer keysChanged] containsObject: @"string2"],
"string2 has been observed");

[observer reset];
[array removeObserver: observer
fromObjectsAtIndexes: idxs
forKeyPath: @"string2"
context: 0];

// Determine if it still responds..
[obj setString2: @"Test"];
PASS([[observer keysChanged] containsObject: @"string2"] == NO,
"string2 should NOT have been observed");


[arp release]; arp = nil;
return 0;
}

0 comments on commit e58b83c

Please sign in to comment.