Skip to content

Instantly share code, notes, and snippets.

@kylesluder
Last active August 29, 2015 13:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kylesluder/8779425 to your computer and use it in GitHub Desktop.
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)
// 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