用于修改由NSMutableArray支持的NSArray的KVO通知 [英] KVO Notifications for a Modification of an NSArray backed by a NSMutableArray

查看:106
本文介绍了用于修改由NSMutableArray支持的NSArray的KVO通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用KVO来收听NSArray属性上的集合更改事件。在公开场合,该属性是一个只读NSArray,但由NSMutableArray ivar支持,以便我可以修改集合。

I am trying to use KVO to listen to collection change events on an NSArray property. Publicly, the property is a readonly NSArray, but is backed by an NSMutableArray ivar so that I can modify the collection.

我知道我可以将属性设置为新值获得设置更改,但我有兴趣添加,删除,替换更改。如何正确通知NSArray的这些类型的更改?

I know I can set the property to a new value to get a "set" change, but I’m interested in add, remove, replace changes. How do I correctly notify these type of changes for an NSArray?

@interface Model : NSObject

@property (nonatomic, readonly) NSArray *items;

@end

@implementation Model {
    NSMutableArray *_items;
}

- (NSArray *)items {
    return [_items copy];
}

- (void)addItem:(Item *)item {
  [_items addObject:item];
}

@end

Model *model = [[Model alloc] init];

[observer addObserver:model 
      forKeyPath:@"items" 
         options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
         context:NULL];

Item *item = [[Item alloc] init];
[model addItem:newItem];

观察员类:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"items"]) {
        //Not called
    }
}


推荐答案

首先,您应该了解KVO用于观察对象以了解其属性的变化。也就是说,您不能观察数组,您会观察索引的集合属性。该属性可以由数组支持或以其他方式实现。只要它符合KVC并以符合KVO标准的方式进行修改,这就足够了。 (因此,如果属性类型为 NSArray * 或使用 NSMutableArray * 或其他任何内容实现,则无关紧要。)

First, you should understand that KVO is for observing an object for changes in its properties. That is, you can't "observe an array" as such, you observe an indexed collection property. That property may be backed by an array or implemented in some other way. So long as it is compliant with KVC and modified in a KVO-compliant manner, that's enough. (So, it doesn't matter if the property is of type NSArray* or implemented using an NSMutableArray* or anything.)

因此,您正在观察 Model 的实例,以了解其中的更改items property。如果您希望观察者获得更改通知,您必须确保始终以符合KVO的方式修改 items 属性。

So, you're observing an instance of Model for changes in its items property. If you want the observer to get a change notification, you have to be sure to always modify the items property in a KVO-compliant manner.

在我看来,最好的方法是实现可变索引集合访问器并始终使用它们来修改属性。所以,你要实现至少其中一个:

The best way, in my opinion, is to implement the mutable indexed collection accessors and always use those to modify the property. So, you'd implement at least one of these:

- (void) insertObject:(id)anObject inItemsAtIndex:(NSUInteger)index;
- (void) insertItems:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;

其中一个:

- (void) removeObjectFromItemsAtIndex:(NSUInteger)index;
- (void) removeItemsAtIndexes:(NSIndexSet *)indexes;

当属性由 NSMutableArray 支持时,上面的方法是围绕 _items 上相应方法的简单包装。

When the property is backed by an NSMutableArray, the above methods are straightforward wrappers around the corresponding methods on _items.

你编写的任何其他方法来修改你的财产应该通过其中之一。因此,您的 -addItem:方法将是:

Any other methods you write to modify your property should go through one of those. So, your -addItem: method would be:

- (void)addItem:(Item *)item {
    [self insertObject:item inItemsAtIndex:[_items count]];
}

您还可以删除项目的普通getter 属性,而只是公开索引的集合getter:

You could also remove the plain getter for the items property and instead only expose the indexed collection getters:

- (NSUInteger) countOfItems;
- (id) objectInItemsAtIndex:(NSUInteger)index;

但是,如果有一个典型的吸气剂,则没有必要。

That's not necessary, though, if there is a typical getter.

(这些访问器的存在允许你实现一个非 NSArray 类型的to-many属性。没有必要,来自KVC的对于任何实际的数组类型接口来说。)

(The existence of those accessors is what allows you to implement a to-many property that is not of type NSArray. There's no need, from KVC's point of view, for any actual array-typed interface.)

我个人不建议这样做,但是一旦你有这样的访问器,你也可以改变属性通过使用 -mutableArrayValueForKey:获取属性的 NSMutableArray -like代理,然后向其发送变异操作。因此,在这种情况下,您可以执行 [[self mutableArrayValueForKey:@items] addObject:item] 。我不喜欢这个,因为我觉得键值编码是指密钥是数据的时候。它是动态的或存储在像NIB这样的数据文件中,在编译时不知道。当您可以选择使用语言符号(例如选择器)来处理属性时,硬编码键名是一种代码气味。

Personally, I don't recommend this, but once you have such accessors, you can also mutate the property by obtaining the NSMutableArray-like proxy for the property using -mutableArrayValueForKey: and then sending mutation operations to it. So, in this case, you might do [[self mutableArrayValueForKey:@"items"] addObject:item]. I don't like this because I feel that key-value coding is for when the key is data. It's dynamic or stored in a data file like a NIB, not known at compile time. Hard-coding key names when you have the option to use a language symbol (e.g. selector) to address the property is a code smell.

尽管如此,它可以被证明是合理的。对于在索引访问器方面实现真正曲折的操作,例如排序。

It can be justified, though, for operations that are truly tortuous to implement in terms of the indexed accessors, such as sorting.

最后,您可以使用 NSKeyValueObserving protocol's -willChange ... -didChange ... 当您直接修改属性的后备存储而不通过时发出更改通知的方法KVO可识别并挂钩的变异方法。对于索引集合属性,可以是 -willChange:valuesAtIndexes:forKey: -didChange:valuesAtIndexes:forKey:方法。就我而言,这是一种更糟糕的代码味道。

Finally, you can use the NSKeyValueObserving protocol's -willChange... and -didChange... methods to emit change notifications when you directly modify the property's backing storage without going through a mutation method that KVO can recognize and hook into. For an indexed collection property, that would be the -willChange:valuesAtIndexes:forKey: and -didChange:valuesAtIndexes:forKey: methods. This is an even worse code smell, as far as I'm concerned.

这篇关于用于修改由NSMutableArray支持的NSArray的KVO通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆