mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +00:00
Update ChangeLog and fix coding style and leaks in testcases
This commit is contained in:
parent
05628229e4
commit
b3d5c20277
3 changed files with 287 additions and 213 deletions
11
ChangeLog
11
ChangeLog
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue