【问题标题】:Observing count in NSMutableArray在 NSMutableArray 中观察计数
【发布时间】:2012-04-07 23:43:01
【问题描述】:

我希望在计数时收到通知,即。 NSArray 中的项目数更改.. 当然,如果我控制在数组中添加和删除对象,我就不需要这个。但我不是,它在业务流程模型方面发生不可预测,并且取决于外部因素。 有没有一些简单优雅的解决方案?

编辑:当然,我正在将其更正为 NSMutableArray..

【问题讨论】:

标签: objective-c ios nsarray


【解决方案1】:

您需要使用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

如果您没有机会像这样包装数组,请尝试重新考虑您的代码。如果外部依赖迫使您陷入这种困境,请尝试将其删除。围绕自己的工具工作总是一件坏事。

【讨论】:

  • 一段时间过去了,幸运的改进将在我无法控制的部分代码上实现 - 保存数组的外观对象现在将在模型更改时发出 NSNotifications。您提出的解决方案值得注意,但是您错过了一个小但重要的事实,即我无法控制阵列。所以我不可能将代理对象注入到外观而不是数组。
  • @CodaFi 感谢您的解释:)
【解决方案2】:

要观察 mutableArray 的变化,需要使用

给出的可变代理对象
 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key

这是符合 KVO 的,即代理对象的任何更改都会发送 will/did 更改通知。

下面的演示类展示了完整的实现

@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 _itemsarray 的任何直接更改都将无效。

另请注意,您强烈需要将观察者设置为_items.@count(观察items.@count 是毫无意义的)。

请注意,您无需初始化 _itemsself.items。当您调用items getter 时,它将在幕后完成。

每次更改“数组”items 时,您都会获得带有新地址的新对象 _items。但我仍然可以通过 items 代理 getter 找到它。

【讨论】:

  • 您可能还需要removeProxyItemsObserver: 方法。
  • @AaronBrager,为了完整性而添加。谢谢。
  • 为什么不在你的示例代码中添加 mutableArrayValueForKey 的使用,这是很重要的一点!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
相关资源
最近更新 更多