Update ChangeLog and fix coding style and leaks in testcases

This commit is contained in:
Hugo Melder 2025-02-04 07:46:34 +01:00 committed by rfm
parent 05628229e4
commit b3d5c20277
3 changed files with 287 additions and 213 deletions

View file

@ -25,6 +25,17 @@
* Documentation/news.texi:
Update release notes for 1.31.0 release.
2025-02-04 Hugo Melder <hugo@algoriddim.com>
* Source/NSKVOSupport.m:
* Tests/base/NSKVOSupport/legacy.m:
Implements the setKeys:triggerChangeNotificationsForDependentKey:
class method. Please do not use it. It is fundamentally broken, and
requires the object's meta class to hold additional state.
Keys from this class method are the last resort when retrieving
dependencies via keyPathsForValuesAffectingValueForKey:.
This aligns with the implementation in Foundation.
2025-01-04 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/Foundation/NSRegularExpression.h:

View file

@ -11,9 +11,9 @@
@interface Observee : NSObject
{
NSString *_firstName;
NSString *_middleName;
NSString *_lastName;
NSString *_firstName;
NSString *_middleName;
NSString *_lastName;
}
- (NSString *) firstName;
@ -32,7 +32,7 @@
@interface ObserveeMixed : Observee
{
BOOL _trigger;
BOOL _trigger;
}
- (BOOL) trigger;
@ -42,43 +42,49 @@
@implementation Observee
- (instancetype)init {
self = [super init];
_firstName = _middleName = _lastName = @"";
- (instancetype) init
{
if ((self = [super init]) != nil)
{
_firstName = _middleName = _lastName = @"";
}
return self;
}
- (NSString *) firstName
{
return _firstName;
return _firstName;
}
- (void) setFirstName: (NSString *)name
{
ASSIGN(_firstName, name);
ASSIGN(_firstName, name);
}
- (NSString *) middleName
{
return _middleName;
return _middleName;
}
- (void) setMiddleName: (NSString *)name
{
ASSIGN(_middleName, name);
ASSIGN(_middleName, name);
}
- (NSString *) lastName
{
return _lastName;
return _lastName;
}
- (void) setLastName: (NSString *)name
{
ASSIGN(_lastName, name);
ASSIGN(_lastName, name);
}
- (NSString *) shortFullName {
return [NSString stringWithFormat: @"%@ %@", _firstName, _lastName];
- (NSString *) shortFullName
{
return [NSString stringWithFormat: @"%@ %@", _firstName, _lastName];
}
- (NSString *) fullName {
return [NSString stringWithFormat: @"%@ %@ %@", _firstName, _middleName, _lastName];
- (NSString *) fullName
{
return [NSString stringWithFormat: @"%@ %@ %@",
_firstName, _middleName, _lastName];
}
@end
@ -87,32 +93,32 @@ return [NSString stringWithFormat: @"%@ %@ %@", _firstName, _middleName, _lastNa
- (BOOL) trigger
{
return _trigger;
return _trigger;
}
- (void) setTrigger: (BOOL) trigger
{
_trigger = trigger;
_trigger = trigger;
}
// We expect this function to have priority over the legacy API
+ (NSSet *)keyPathsForValuesAffectingFullName
+ (NSSet *) keyPathsForValuesAffectingFullName
{
return [NSSet setWithObject:@"trigger"];
return [NSSet setWithObject: @"trigger"];
}
@end
@interface Observer : NSObject
{
@public
NSString *lastKeyPath;
id lastObject;
NSDictionary *lastChange;
int notificationCount;
}
@end
@implementation Observer
{
@public
NSString *lastKeyPath;
id lastObject;
NSDictionary *lastChange;
int notificationCount;
}
- (void) resetValues
{
@ -123,13 +129,13 @@ return [NSString stringWithFormat: @"%@ %@ %@", _firstName, _middleName, _lastNa
- (void) resetCounter
{
notificationCount = 0;
notificationCount = 0;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
notificationCount += 1;
lastKeyPath = keyPath;
@ -147,43 +153,49 @@ void simpleDependency(void)
NSArray *keys = [NSArray arrayWithObjects: @"firstName", @"lastName", nil];
[Observee setKeys: keys
triggerChangeNotificationsForDependentKey:@"fullName"];
triggerChangeNotificationsForDependentKey: @"fullName"];
NSSet *s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"];
NSSet *s = [Observee keyPathsForValuesAffectingValueForKey: @"fullName"];
NSSet *expectedSet = [NSSet setWithArray: keys];
PASS_EQUAL(s, expectedSet, "'keyPathsForValuesAffectingValueForKey:' returns the correct values affecting 'fullName'");
PASS_EQUAL(s, expectedSet, "'keyPathsForValuesAffectingValueForKey:' returns"
" the correct values affecting 'fullName'")
NSKeyValueObservingOptions opts =
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
NSKeyValueObservingOptions opts
= NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL];
[e addObserver: o forKeyPath: @"fullName" options: opts context: NULL];
[e setFirstName:@"Hey"];
[e setFirstName: @"Hey"];
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'");
PASS_EQUAL(o->lastObject, e, "last change object is correct");
PASS(o->lastChange != nil, "last change is not nil");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey ", "new entry in change dict correct");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @" ", "old entry in change dict correct");
PASS(o->notificationCount == 1, "notification count is 1");
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'")
PASS_EQUAL(o->lastObject, e, "last change object is correct")
PASS(o->lastChange != nil, "last change is not nil")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey ",
"new entry in change dict correct")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @" ",
"old entry in change dict correct")
PASS(o->notificationCount == 1, "notification count is 1")
[e setMiddleName:@"Not"]; // no change notification
[e setMiddleName: @"Not"]; // no change notification
[e setLastName:@"You"];
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'");
PASS_EQUAL(o->lastObject, e, "last change object is correct");
PASS(o->lastChange != nil, "last change is not nil");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey Not You", "new entry in change dict correct");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey Not ", "old entry in change dict correct");
PASS(o->notificationCount == 2, "notification count is 2");
[e setLastName: @"You"];
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'")
PASS_EQUAL(o->lastObject, e, "last change object is correct")
PASS(o->lastChange != nil, "last change is not nil")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey],
@"Hey Not You", "new entry in change dict correct")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey],
@"Hey Not ", "old entry in change dict correct")
PASS(o->notificationCount == 2, "notification count is 2")
[e removeObserver:o forKeyPath:@"fullName"];
[e removeObserver: o forKeyPath: @"fullName"];
[o release];
[e release];
RELEASE(o);
RELEASE(e);
}
void registeringMultipleDependencies(void) {
void registeringMultipleDependencies(void)
{
Observer *o;
Observee *e;
NSArray *arr;
@ -194,72 +206,89 @@ void registeringMultipleDependencies(void) {
arr = [NSArray arrayWithObject: @"firstName"];
[Observee setKeys:arr
triggerChangeNotificationsForDependentKey:@"fullName"];
s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' as affecting key for 'fullName'");
triggerChangeNotificationsForDependentKey: @"fullName"];
s = [Observee keyPathsForValuesAffectingValueForKey: @"fullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' as"
" affecting key for 'fullName'")
arr = [NSArray arrayWithObject: @"middleName"];
[Observee setKeys: arr
triggerChangeNotificationsForDependentKey:@"fullName"];
s = [Observee keyPathsForValuesAffectingValueForKey:@"fullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'middleName' as affecting key for 'fullName'");
triggerChangeNotificationsForDependentKey: @"fullName"];
s = [Observee keyPathsForValuesAffectingValueForKey: @"fullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'middleName' as"
" affecting key for 'fullName'")
arr = [NSArray arrayWithObjects: @"firstName", @"lastName", nil];
[Observee setKeys:arr
triggerChangeNotificationsForDependentKey:@"shortFullName"];
s = [Observee keyPathsForValuesAffectingValueForKey:@"shortFullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' and 'lastName' as affecting key for 'fullName'");
[Observee setKeys: arr
triggerChangeNotificationsForDependentKey: @"shortFullName"];
s = [Observee keyPathsForValuesAffectingValueForKey: @"shortFullName"];
PASS_EQUAL(s, [NSSet setWithArray: arr], "expecting 'firstName' and"
" 'lastName' as affecting key for 'fullName'")
NSKeyValueObservingOptions opts =
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
NSKeyValueObservingOptions opts
= NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL];
[e addObserver: o forKeyPath: @"fullName" options: opts context: NULL];
[e setFirstName:@"Hey"];
PASS(o->notificationCount == 0, "no change notification received when modifying firstName");
[e setFirstName: @"Hey"];
PASS(o->notificationCount == 0, "no change notification received when"
" modifying firstName")
[e setMiddleName:@"Not"];
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'");
PASS_EQUAL(o->lastObject, e, "last change object is correct");
PASS(o->lastChange != nil, "last change is not nil");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hey Not ", "new entry in change dict correct");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey ", "old entry in change dict correct");
PASS(o->notificationCount == 1, "change notification received when modifying middleName");
[e setMiddleName: @"Not"];
PASS_EQUAL(o->lastKeyPath, @"fullName", "last keypath is 'fullName'")
PASS_EQUAL(o->lastObject, e, "last change object is correct")
PASS(o->lastChange != nil, "last change is not nil")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey],
@"Hey Not ", "new entry in change dict correct")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey],
@"Hey ", "old entry in change dict correct")
PASS(o->notificationCount == 1, "change notification received when"
" modifying middleName")
[e setLastName:@"You"];
PASS(o->notificationCount == 1, "no change notification received when modifying lastName");
[e setLastName: @"You"];
PASS(o->notificationCount == 1, "no change notification received when"
" modifying lastName")
[e removeObserver:o forKeyPath:@"fullName"];
[e removeObserver: o forKeyPath: @"fullName"];
[o resetCounter];
[o resetValues];
[e addObserver:o forKeyPath:@"shortFullName" options:opts context:NULL];
[e addObserver: o forKeyPath: @"shortFullName" options: opts context: NULL];
[e setFirstName:@"Hello"];
PASS_EQUAL(o->lastKeyPath, @"shortFullName", "last keypath is 'shortFullName'");
PASS_EQUAL(o->lastObject, e, "last change object is correct");
PASS(o->lastChange != nil, "last change is not nil");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hello You", "new entry in change dict correct");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hey You", "old entry in change dict correct");
PASS(o->notificationCount == 1, "change notification received when modifying firstName");
[e setFirstName: @"Hello"];
PASS_EQUAL(o->lastKeyPath, @"shortFullName",
"last keypath is 'shortFullName'")
PASS_EQUAL(o->lastObject, e, "last change object is correct")
PASS(o->lastChange != nil, "last change is not nil")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey],
@"Hello You", "new entry in change dict correct")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey],
@"Hey You", "old entry in change dict correct")
PASS(o->notificationCount == 1, "change notification received when"
" modifying firstName")
[e setMiddleName:@"Not"];
PASS(o->notificationCount == 1, "no change notification received when modifying middleName");
[e setMiddleName: @"Not"];
PASS(o->notificationCount == 1, "no change notification received when"
" modifying middleName")
[o resetValues];
[e setLastName:@"World"];
PASS_EQUAL(o->lastKeyPath, @"shortFullName", "last keypath is 'shortFullName'");
PASS_EQUAL(o->lastObject, e, "last change object is correct");
PASS(o->lastChange != nil, "last change is not nil");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey], @"Hello World", "new entry in change dict correct");
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey], @"Hello You", "old entry in change dict correct");
PASS(o->notificationCount == 2, "change notification received when modifying lastName");
[e setLastName: @"World"];
PASS_EQUAL(o->lastKeyPath, @"shortFullName",
"last keypath is 'shortFullName'")
PASS_EQUAL(o->lastObject, e, "last change object is correct")
PASS(o->lastChange != nil, "last change is not nil")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeNewKey],
@"Hello World", "new entry in change dict correct")
PASS_EQUAL([o->lastChange valueForKey: NSKeyValueChangeOldKey],
@"Hello You", "old entry in change dict correct")
PASS(o->notificationCount == 2, "change notification received when"
" modifying lastName")
[e removeObserver:o forKeyPath:@"shortFullName"];
[e removeObserver: o forKeyPath: @"shortFullName"];
[o release];
[e release];
RELEASE(o);
RELEASE(e);
}
void mixedLegacy(void)
@ -269,47 +298,56 @@ void mixedLegacy(void)
NSArray *keys = [NSArray arrayWithObjects: @"firstName", @"lastName", nil];
[ObserveeMixed setKeys:keys
triggerChangeNotificationsForDependentKey:@"fullName"];
[ObserveeMixed setKeys: keys
triggerChangeNotificationsForDependentKey: @"fullName"];
NSSet *s = [ObserveeMixed keyPathsForValuesAffectingValueForKey:@"fullName"];
NSSet *expected = [NSSet setWithObject:@"trigger"];
PASS_EQUAL(s, expected, "newer API has precedence over deprecated API");
NSSet *s = [ObserveeMixed keyPathsForValuesAffectingValueForKey: @"fullName"];
NSSet *expected = [NSSet setWithObject: @"trigger"];
PASS_EQUAL(s, expected, "newer API has precedence over deprecated API")
NSKeyValueObservingOptions opts =
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
NSKeyValueObservingOptions opts
= NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
[e addObserver:o forKeyPath:@"fullName" options:opts context:NULL];
[e addObserver: o forKeyPath: @"fullName" options: opts context: NULL];
// No trigger
[e setFirstName:@"Hey"];
[e setMiddleName:@"Not"];
[e setLastName:@"You"];
PASS(o->notificationCount == 0, "no change notification from either firstName, middleName, or lastName");
[e setFirstName: @"Hey"];
[e setMiddleName: @"Not"];
[e setLastName: @"You"];
PASS(o->notificationCount == 0, "no change notification from either"
" firstName, middleName, or lastName")
// Trigger
[e setTrigger:YES];
PASS(o->notificationCount == 1, "change notification from trigger");
[e setTrigger: YES];
PASS(o->notificationCount == 1, "change notification from trigger")
[e removeObserver:o forKeyPath:@"fullName"];
[o release];
[e release];
[e removeObserver: o forKeyPath: @"fullName"];
RELEASE(o);
RELEASE(e);
}
int
main(int argc, char *argv[])
{
START_SET("KVO Legacy Tests")
START_SET("KVO Legacy Tests")
simpleDependency();
registeringMultipleDependencies();
mixedLegacy();
#if defined(__GNUC__)
testHopeful = YES;
#endif
END_SET("KVO Legacy Tests")
simpleDependency();
registeringMultipleDependencies();
mixedLegacy();
return 0;
#if defined(__GNUC__)
testHopeful = YES;
#endif
END_SET("KVO Legacy Tests")
return 0;
}

View file

@ -12,15 +12,16 @@
#import "Testing.h"
/**
* The new KVO implementation for libobjc2/clang, located in Source/NSKVO*, reuses
* or installs a hidden class and subsequently adds the swizzled method to the
* hidden class. Make sure that the invocation mechanism calls the swizzled method.
* The new KVO implementation for libobjc2/clang, located in Source/NSKVO*,
* reuses or installs a hidden class and subsequently adds the swizzled
* method to the hidden class. Make sure that the invocation mechanism calls
* the swizzled method.
*/
@interface Observee : NSObject
{
NSString *_name;
NSString *_derivedName;
NSString *_name;
NSString *_derivedName;
}
- (NSString *) name;
@ -35,38 +36,39 @@
- (NSString *) name
{
return [[_name retain] autorelease];
return AUTORELEASE(RETAIN(_name));
}
- (void) setName: (NSString *)name
{
ASSIGN(_name, name);
}
- (NSString *)derivedName
- (NSString *) derivedName
{
return [NSString stringWithFormat:@"Derived %@", self.name];
return [NSString stringWithFormat: @"Derived %@", [self name]];
}
- (void) setDerivedName: (NSString *)name
{
ASSIGN(_derivedName, name);
ASSIGN(_derivedName, name);
}
+ (NSSet *)keyPathsForValuesAffectingDerivedName
+ (NSSet *) keyPathsForValuesAffectingDerivedName
{
return [NSSet setWithObject:@"name"];
return [NSSet setWithObject: @"name"];
}
- (void) dealloc {
RELEASE(_name);
RELEASE(_derivedName);
[super dealloc];
- (void) dealloc
{
RELEASE(_name);
RELEASE(_derivedName);
DEALLOC
}
@end
@interface TProxy : NSProxy
{
id _proxiedObject;
id _proxiedObject;
}
@end
@ -74,31 +76,31 @@
- (instancetype)initWithProxiedObject:(id)proxiedObject
{
ASSIGN(_proxiedObject, proxiedObject);
return self;
ASSIGN(_proxiedObject, proxiedObject);
return self;
}
- (void)forwardInvocation:(NSInvocation *)invocation
- (void) forwardInvocation: (NSInvocation *)invocation
{
[invocation invokeWithTarget:_proxiedObject];
[invocation invokeWithTarget: _proxiedObject];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
- (NSMethodSignature*) methodSignatureForSelector: (SEL)sel
{
return [_proxiedObject methodSignatureForSelector:sel];
return [_proxiedObject methodSignatureForSelector: sel];
}
- (void) dealloc
{
RELEASE(_proxiedObject);
[super dealloc];
RELEASE(_proxiedObject);
DEALLOC
}
@end
@interface Wrapper : NSObject
{
TProxy *_proxy;
TProxy *_proxy;
}
- (instancetype) initWithProxy: (TProxy *) proxy;
@ -111,105 +113,128 @@
- (instancetype) initWithProxy: (TProxy *) proxy
{
self = [super init];
if (self)
self = [super init];
if (self)
{
_proxy = proxy;
_proxy = proxy;
}
return self;
return self;
}
- (TProxy *) proxy
{
return _proxy;
return _proxy;
}
@end
@interface Observer: NSObject
{
int count;
NSArray *keys;
int count;
NSArray *keys;
}
- (void)runTest;
@end
@implementation Observer
- (void)simpleKeypathTest
- (void) simpleKeypathTest
{
Observee *obj = [[Observee alloc] init];
TProxy *proxy = [[TProxy alloc] initWithProxiedObject:obj];
Observee *obj = [[Observee alloc] init];
TProxy *proxy = [[TProxy alloc] initWithProxiedObject: obj];
keys = [NSArray arrayWithObjects: @"derivedName", @"name", nil];
count = 0;
keys = [NSArray arrayWithObjects: @"derivedName", @"name", nil];
count = 0;
[(Observee *)proxy addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
[(Observee *)proxy addObserver:self forKeyPath:@"derivedName" options:NSKeyValueObservingOptionNew context:NULL];
[(Observee *)proxy addObserver: self
forKeyPath: @"name"
options: NSKeyValueObservingOptionNew
context: NULL];
[(Observee *)proxy addObserver: self
forKeyPath: @"derivedName"
options: NSKeyValueObservingOptionNew
context: NULL];
[((Observee *)proxy) setName: @"MOO"];
PASS(count == 2, "Got two change notifications");
[((Observee *)proxy) setName: @"MOO"];
PASS(count == 2, "Got two change notifications");
[obj setName: @"BAH"];
PASS(count == 4, "Got two change notifications");
[obj setName: @"BAH"];
PASS(count == 4, "Got two change notifications");
[(Observee *)proxy removeObserver:self forKeyPath:@"name" context:NULL];
[(Observee *)proxy removeObserver:self forKeyPath:@"derivedName" context:NULL];
[(Observee *)proxy removeObserver: self forKeyPath: @"name" context: NULL];
[(Observee *)proxy removeObserver: self
forKeyPath: @"derivedName"
context: NULL];
[proxy release];
[obj release];
RELEASE(proxy);
RELEASE(obj);
}
- (void)nestedKeypathTest
- (void) nestedKeypathTest
{
Observee *obj = [[Observee alloc] init];
TProxy *proxy = [[TProxy alloc] initWithProxiedObject:obj];
Observee *obj = [[Observee alloc] init];
TProxy *proxy = [[TProxy alloc] initWithProxiedObject: obj];
Wrapper *w = [[Wrapper alloc] initWithProxy: proxy];
keys = [NSArray arrayWithObjects: @"proxy.derivedName", @"proxy.name", nil];
count = 0;
[w addObserver:self forKeyPath:@"proxy.name" options:NSKeyValueObservingOptionNew context:NULL];
[w addObserver:self forKeyPath:@"proxy.derivedName" options:NSKeyValueObservingOptionNew context:NULL];
[w addObserver: self
forKeyPath: @"proxy.name"
options: NSKeyValueObservingOptionNew
context: NULL];
[w addObserver: self
forKeyPath: @"proxy.derivedName"
options: NSKeyValueObservingOptionNew
context: NULL];
[((Observee *)proxy) setName: @"MOO"];
[((Observee *)proxy) setName: @"MOO"];
PASS(count == 2, "Got two change notifications");
[obj setName: @"BAH"];
[obj setName: @"BAH"];
PASS(count == 4, "Got two change notifications");
[w removeObserver:self forKeyPath:@"proxy.name" context:NULL];
[w removeObserver:self forKeyPath:@"proxy.derivedName" context:NULL];
[w removeObserver: self forKeyPath: @"proxy.name" context: NULL];
[w removeObserver: self forKeyPath: @"proxy.derivedName" context: NULL];
[w release];
[proxy release];
[obj release];
RELEASE(w);
RELEASE(proxy);
RELEASE(obj);
count = 0;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
{
count += 1;
switch (count) {
case 1:
PASS_EQUAL(keyPath, [keys objectAtIndex: 0], "change notification for dependent key 'derivedName' is emitted first");
break;
case 2:
PASS_EQUAL(keyPath, [keys objectAtIndex: 1], "'name' change notification for proxy is second");
break;
case 3:
PASS_EQUAL(keyPath, [keys objectAtIndex: 0], "'derivedName' change notification for object is third");
break;
case 4:
PASS_EQUAL(keyPath, [keys objectAtIndex: 1], "'name' change notification for object is fourth");
break;
default:
PASS(0, "unexpected -[Observer observeValueForKeyPath:ofObject:change:context:] callback");
count += 1;
switch (count)
{
case 1:
PASS_EQUAL(keyPath, [keys objectAtIndex: 0],
"change notification for dependent key 'derivedName'"
" is emitted first")
break;
case 2:
PASS_EQUAL(keyPath, [keys objectAtIndex: 1],
"'name' change notification for proxy is second")
break;
case 3:
PASS_EQUAL(keyPath, [keys objectAtIndex: 0],
"'derivedName' change notification for object is third")
break;
case 4:
PASS_EQUAL(keyPath, [keys objectAtIndex: 1],
"'name' change notification for object is fourth")
break;
default:
PASS(0,
"unexpected -[Observer observeValueForKeyPath:ofObject:"
"change:context:] callback")
}
}
@ -218,15 +243,15 @@
int
main(int argc, char *argv[])
{
START_SET("KVO Proxy Tests")
Observer *obs = [Observer new];
START_SET("KVO Proxy Tests")
Observer *obs = [Observer new];
testHopeful = YES;
[obs simpleKeypathTest];
[obs nestedKeypathTest];
testHopeful = NO;
testHopeful = YES;
[obs simpleKeypathTest];
[obs nestedKeypathTest];
testHopeful = NO;
[obs release];
END_SET("KVO Proxy Tests")
return 0;
RELEASE(obs);
END_SET("KVO Proxy Tests")
return 0;
}