Merge pull request #330 from gnustep/NSKeyValueObserving_issue327

This commit is contained in:
Gregory Casamento 2023-10-08 05:49:59 -04:00 committed by GitHub
commit e58b83c1f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 1 deletions

4
.gitignore vendored
View file

@ -9,6 +9,10 @@ Tests/base/*/GNUmakefile
*.log
*.sum
# Unit test byproducts
*.err
*.out
# Autoconf
autom4te.cache

View file

@ -1,3 +1,12 @@
2023-10-07 Gregory John Casamento <greg.casamento@gmail.com>
* 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 <rfm@gnu.org>
* Headers/Foundation/NSCache.h: Add _lock ivar

View file

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

View file

@ -1687,6 +1687,25 @@ cifframe_callback(ffi_cif *cif, void *retp, void **args, void *user)
}
}
- (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
/**

View file

@ -8,6 +8,14 @@ typedef struct {
} aStruct;
@interface Observer : NSObject
{
NSMutableSet *_keysChanged;
}
- (void) reset;
- (NSSet *) keysChanged;
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
@ -15,6 +23,32 @@ typedef struct {
@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
@ -22,6 +56,8 @@ typedef struct {
{
NSLog(@"observeValueForKeyPath: %@\nofObject:%@\nchange:%@\ncontext:%p",
keyPath, object, change, context);
[_keysChanged addObject: keyPath];
}
@end
@ -31,6 +67,7 @@ typedef struct {
NSMutableArray * numbers;
NSMutableArray * third;
NSString *string;
NSString *string2;
aStruct x;
}
@ -115,6 +152,16 @@ typedef struct {
x = s;
}
- (void) setString2: (NSString *)s
{
string2 = s;
}
- (NSString *) string2
{
return string2;
}
- (void) willChangeValueForKey: (NSString*)k
{
[super willChangeValueForKey: k];
@ -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]],
@ -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;
}