I'd like to implement KVO for an NSArray
property that is declared as readonly
. The getter for this readonly
property returns a copy of the private NSMutableArray
that backs the backs the public readonly
one:
In my .h
:
@interface MyClass : NSObject
@property (readonly, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
@end
And in my .m
:
@interface MyClass()
@property (strong, nonatomic) NSMutableArray *myPrivateArray;
@end
@implementation MyClass
- (NSArray *)myArray {
return (NSArray *)[self.myPrivateArray copy];
}
- (void) addObjectToMyArray:(NSObject *)obj {
[self willChangeValueForKey:@"myArray"];
[self.myPrivateArray addObject:obj];
[self didChangeValueForKey:@"myArray"];
}
- (void) removeObjectToMyArray:(NSObject *)obj {
[self willChangeValueForKey:@"myArray"];
[self.myPrivateArray removeObject:obj];
[self didChangeValueForKey:@"myArray"];
}
@end
In my tests, I am seeing an exception thrown when I call didChangeValueForKey:
. Is this the correct way to do this?
I recommend that you don't use a separate property for the mutable array. Instead, have the array property backed by a mutable array variable. Then, implement the indexed collection mutating accessors and make all changes to the array through those. KVO knows to hook into those accessors and emit change notifications. In fact, it can emit better, more specific change notifications that can allow observers to be more efficient in how they respond.
@interface MyClass : NSObject
@property (readonly, copy, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
@end
@interface MyClass()
// Optional, if you want to be able to do self.myArray = <whatever> in your implementation
@property (readwrite, copy, nonatomic) NSArray *myArray;
@end
@implementation MyClass
{
NSMutableArray *_myArray;
}
@synthesize myArray = _myArray;
// If you optionally re-declared the property read-write internally, above
- (void) setMyArray:(NSArray*)array {
if (array != _myArray) {
_myArray = [array mutableCopy];
}
}
- (void) insertObject:(id)anObject inMyArrayAtIndex:(NSUInteger)index {
[_myArray insertObject:anObject atIndex:index];
}
- (void) removeObjectFromMyArrayAtIndex:(NSUInteger)index {
[_myArray removeObjectAtIndex:index];
}
- (void) addObjectToMyArray:(NSObject *)obj {
[self insertObject:obj inMyArrayAtIndex:_myArray.count];
}
- (void) removeObjectToMyArray:(NSObject *)obj {
NSUInteger index = [_myArray indexOfObject:obj];
if (index != NSNotFound)
[self removeObjectFromMyArrayAtIndex:index];
}
@end