Last active
August 29, 2015 13:55
-
-
Save kylesluder/8779425 to your computer and use it in GitHub Desktop.
A demonstration of an inefficiency in mutable array proxies that I think could be improved (rdar://problem/15966921)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// clang -framework Foundation -o AutoNotifyTest AutoNotifyTest.m | |
/*** | |
Expected results: | |
willChange:valuesAtIndexes:forKey: | |
removeObjectFromBarAtIndex: | |
removeObjectFromBarAtIndex: | |
removeObjectFromBarAtIndex: | |
removeObjectFromBarAtIndex: | |
insertObject:inBarAtIndex: | |
insertObject:inBarAtIndex: | |
insertObject:inBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
Actual results: | |
willChange:valuesAtIndexes:forKey: | |
removeObjectFromBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
removeObjectFromBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
removeObjectFromBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
removeObjectFromBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
insertObject:inBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
insertObject:inBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
willChange:valuesAtIndexes:forKey: | |
insertObject:inBarAtIndex: | |
didChange:valuesAtIndexes:forKey: | |
-- Observed change. | |
***/ | |
#import <Foundation/Foundation.h> | |
@interface Foo : NSObject | |
// KVO-compliant for mutable array property `bar` | |
@end | |
@implementation Foo | |
{ | |
NSMutableArray *_bar; | |
} | |
- (instancetype)init { | |
if ((self = [super init])) { | |
_bar = [@[@"one", @"two", @"three", @"four"] mutableCopy]; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[_bar release]; | |
[super dealloc]; | |
} | |
- (NSUInteger)countOfBar { | |
return _bar.count; | |
} | |
- (id)objectInBarAtIndex:(NSUInteger)idx { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
return _bar[idx]; | |
} | |
- (NSArray *)barAtIndexes:(NSIndexSet *)indexes { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
return [_bar objectsAtIndexes:indexes]; | |
} | |
- (void)insertObject:(id)o inBarAtIndex:(NSUInteger)idx { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[_bar insertObject:o atIndex:idx]; | |
} | |
- (void)insertBar:(NSArray *)objs atIndexes:(NSIndexSet *)indexes { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[_bar insertObjects:objs atIndexes:indexes]; | |
} | |
- (void)removeObjectFromBarAtIndex:(NSUInteger)idx { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[_bar removeObjectAtIndex:idx]; | |
} | |
- (void)removeBarAtIndexes:(NSIndexSet *)indexes { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[_bar removeObjectsAtIndexes:indexes]; | |
} | |
- (void)willChangeValueForKey:(NSString *)key { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[super willChangeValueForKey:key]; | |
} | |
- (void)willChange:(NSKeyValueChange)change valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[super willChange:change valuesAtIndexes:indexes forKey:key]; | |
} | |
- (void)didChangeValueForKey:(NSString *)key { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[super didChangeValueForKey:key]; | |
} | |
- (void)didChange:(NSKeyValueChange)change valuesAtIndexes:(NSIndexSet *)indexes forKey:(NSString *)key { | |
NSLog(@"%@", NSStringFromSelector(_cmd)); | |
[super didChange:change valuesAtIndexes:indexes forKey:key]; | |
} | |
@end | |
@interface AnObserver : NSObject | |
@property (assign) Foo *f; | |
@end | |
@implementation AnObserver | |
{ | |
Foo *_f; | |
} | |
static void *ctx = &ctx; | |
- (Foo *)f { | |
return _f; | |
} | |
- (void)setF:(Foo *)f { | |
[_f removeObserver:self forKeyPath:@"bar" context:ctx]; | |
[_f release]; | |
_f = [f retain]; | |
[_f addObserver:self forKeyPath:@"bar" options:0 context:ctx]; | |
} | |
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)obj change:(NSDictionary *)change context:(void *)context { | |
if (context == ctx) | |
NSLog(@"-- Observed change."); | |
else | |
[super observeValueForKeyPath:keyPath ofObject:obj change:change context:context]; | |
} | |
@end | |
int main(int argc, char **argv) { | |
@autoreleasepool { | |
Foo *f = [Foo new]; | |
AnObserver *obs = [AnObserver new]; | |
obs.f = f; | |
// This should be much more efficient--while inside the call to -setArray:, the mutable array proxy should send _ONE_ notification, then suspend auto notifications while calling the indexed to-many accessors. | |
NSMutableArray *arr = [f mutableArrayValueForKey:@"bar"]; | |
[arr setArray:@[@"three", @"five", @"one"]]; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment