我想在计数时收到通知,即。NSArray 中的项目数发生了变化。当然,如果我控制在数组中添加和删除对象,我就不需要这个。但我不是,它在业务流程模型方面发生不可预测,并且取决于外部因素。有一些简单优雅的解决方案吗?
编辑:我当然将其更正为 NSMutableArray ..
我想在计数时收到通知,即。NSArray 中的项目数发生了变化。当然,如果我控制在数组中添加和删除对象,我就不需要这个。但我不是,它在业务流程模型方面发生不可预测,并且取决于外部因素。有一些简单优雅的解决方案吗?
编辑:我当然将其更正为 NSMutableArray ..
您需要使用KVC。但是如何去做呢?毕竟,NSMutableArray 不符合 Key-Value-Coding 的变异方法或内容更改。答案是代理——因为子类化 NS[Mutable]Array 太麻烦了。
NSProxy 是一个很棒的小类,您可以使用它来拦截发送到数组的消息,就像您是 NSMutableArray 一样,然后将它们转发到某个内部实例。不幸的是,它也不符合 KVC,因为 KVC 的核心存在于 NSObject 中。那么,我们将不得不使用它。示例界面可能如下所示:
@interface CFIKVCMutableArrayProxy : NSObject {
NSMutableArray *_innerArray;
}
- (NSUInteger)count;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)addObject:(id)anObject;
- (void)removeLastObject;
- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
//…
@end
如您所见,我们正在模拟 的接口NSMutableArray
,这是必要的,因为我们的代理应该像实现NSMutableArray
. 这也使实现尽可能简单,因为我们可以将选择器转发到我们的内部NSMutableArray
指针。为简洁起见,我将仅实现两种方法来向您展示总体轮廓是什么样的:
@implementation CFIKVCMutableArrayProxy
//…
- (NSUInteger)count {
return _innerArray.count;
}
- (void)addObject:(id)anObject {
[self willChangeValueForKey:@"count"];
[_innerArray addObject:anObject];
[self didChangeValueForKey:@"count"];
}
- (void)removeLastObject {
[self willChangeValueForKey:@"count"];
[_innerArray removeLastObject];
[self didChangeValueForKey:@"count"];
}
@end
如果您没有机会像这样包装数组,请尝试重新考虑您的代码。如果外部依赖迫使您陷入这种困境,请尝试将其删除。围绕自己的工具工作总是一件坏事。
要观察 mutableArray 的变化,需要使用由下式给出的可变代理对象
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key
这是 KVO 兼容的,即代理对象的任何更改都会发送将/确实更改通知。
下面的演示类展示了完整的实现
@interface DemoClass : NSObject
@property (nonatomic) NSMutableArray *items;
- (void)addItemsObserver:(id)object;
- (void)removeItemsObserver:(id)object;
@end
@implementation DemoClass
- (NSMutableArray *)items;
{
return [self mutableArrayValueForKey:@"_items"];
}
- (void)addItemsObserver:(id)object
{
[self addObserver:object forKeyPath:@"_items.@count" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
- (void)removeItemsObserver:(id)object
{
[self removeObserver:object forKeyPath:@"_items.@count" context:nil];
}
@end
@interface ObservingClass : NSObject
@property (nonatomic) DemoClass *demoObject;
@end
@implementation ObservingClass
- (instanstype)init
{
if (self = [super init]) {
_demoObject = [DemoClass new];
[_demoObject addItemsObserver:self];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(@"is called on demoObject.items.count change");
}
- (void)dealloc
{
[_demoObject removeItemsObserver:self];
}
@end
现在,每次您在控制台中添加或删除对象时,items
您都会看到新的登录控制台(observeValueForKeyPath
被调用)。
自动合成的 ivar_items
数组的任何直接更改都将无效。
另请注意,您强烈需要设置观察者_items.@count
(观察items.@count
是毫无意义的)。
请注意,您不需要 init_items
或self.items
. items
当您调用getter时,它将在幕后完成。
每次更改“数组”items
时,您都会获得_items
具有新地址的新对象。但我仍然可以通过items
代理吸气剂找到它。