使用 KVO 与 NSNotificationCenter 观察对可变数组的更改 [英] Observing Changes to a mutable array using KVO vs. NSNotificationCenter

查看:20
本文介绍了使用 KVO 与 NSNotificationCenter 观察对可变数组的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的模型中,我有一个称为事件的对象数组.我希望每当将新对象添加到事件时通知我的控制器.

我认为这样做的一个好方法是使用 KVO 模式在事件发生变化时获得通知(来自添加的新对象)

//AppDelegate//events 是一个 NSMutableArray @property/@synthesize 等...[appDelagate addObserver:selfforKeyPath:@"事件"选项:NSKeyValueObservingOptionNew上下文:NULL];

但是 observeValueForKeyPath 方法没有被调用,我发现数组不符合 KVO :-(

一种选择是通过为 keyPath 调用 willChangeValueForKey 来手动触发该方法

//视图控制器[self willChangeValueForKey:@"events"];[self.events addObject:event];[self didChangeValueForKey:@"events"];

但这感觉很重,因为我可能还应该跟踪我的事件数组的前后状态,以便可以从 observeValueForKeyPath 方法访问它.

一种方法可以是使用标准数组(而不是可变数组)并在每次我想添加新对象时创建/设置一个新的事件实例,或者我可以创建一个单独的属性来跟踪有多少项目在可变数组中(我希望你能观察到 @"events.count" ).

另一个选择是使用 NSNotificationCenter.我还阅读了一些建议使用块的答案(但我不知道从哪里开始).

最后,我可以在我的委托中保留我的控制器实例并发送相关消息吗?

//委托[myController eventsDidChange];

保留委托对控制器的引用很奇怪吗?

我正在努力了解如何选择最佳使用方法,因此非常感谢任何有关性能、未来代码灵活性和最佳实践的建议!

解决方案

您不应该为可变集合创建直接的公共属性,以避免它们在您不知情的情况下发生变异.NSArray 本身不是可观察的键值对,但您的一对多 property @"events" 是.观察方法如下:

首先,为不可变集合声明一个公共属性:

@interface 模型@property (nonatomic, copy) NSArray *events;@结尾

然后在你的实现中用一个可变的 ivar 支持它:

@interface 模型 (){NSMutableArray *_events;}@结尾

并覆盖 getter 和 setter:

@implementation 模型@synthesize 事件 = _events;- (NSArray *) 事件{返回 [_events 副本];}- (void)setEvents:(NSArray *)events{if ([_events isEqualToArray:events] == NO){_events = [事件可变复制];}}@结尾

如果其他对象需要给你的模型添加事件,可以通过调用-[Model mutableArrayValueForKey:@"events"]获取可变代理对象.

NSMutableArray *events = [modelInstance mutableArrayValueForKey:@"events"];[事件添加对象:新事件];

这将通过每次使用新集合设置属性来触发 KVO 通知.为了获得更好的性能和更精细的控制,请实现 数组访问器.

另请参阅:观察 NSMutableArray 以进行插入/删除..p>

In my model I have an array of objects called events. I would like my controller to be notified whenever a new object is added to events.

I thought that a good way to do this would be use the KVO pattern to get notified when the events changes (from a new object being added)

// AppDelegate
// events is a NSMutableArray @property/@synthesize etc...

[appDelagate addObserver:self
               forKeyPath:@"events"
                  options:NSKeyValueObservingOptionNew
                  context:NULL];

But the observeValueForKeyPath method wasn't being called and I discovered that Arrays are not KVO compliant :-(

One option is to manually trigger the method by calling willChangeValueForKey for the keyPath

// ViewController
[self willChangeValueForKey:@"events"];
[self.events addObject:event];
[self didChangeValueForKey:@"events"];

But this feels heavy since I should probably also keep track of the before and after state of my events array so that it can be accessed from the observeValueForKeyPath method.

One approach could be to use a standard array (instead of mutable) and create/set a new instance of events each time I want to add an new object, or I could make a separate property that keeps track of how many items are in the mutable array (I wish you could observe @"events.count" ).

Another option would be to use NSNotificationCenter. I've also read some answers that suggest using blocks (but I have no idea where to start on that).

Finally, could I keep an instance of my controller in my delegate and just send a relevant message?

// Delegate
[myController eventsDidChange];

Is it odd to keep a reference to a controller from a delegate?

I'm struggling to understand how to choose which is the best approach to use, so any advice on performance, future code flexibility and best practices is greatly appreciated!

解决方案

You should not make direct public properties for mutable collections to avoid them mutating without your knowledge. NSArray is not key-value observable itself, but your one-to-many property @"events" is. Here's how to observe it:

First, declare a public property for an immutable collection:

@interface Model
@property (nonatomic, copy) NSArray *events;
@end

Then in your implementation back it with a mutable ivar:

@interface Model ()
{
    NSMutableArray *_events;
}
@end

and override the getter and setter:

@implementation Model

@synthesize events = _events;

- (NSArray *)events
{
    return [_events copy];
}

- (void)setEvents:(NSArray *)events
{
    if ([_events isEqualToArray:events] == NO)
    {
        _events = [events mutableCopy];
    }
}

@end

If other objects need to add events to your model, they can obtain a mutable proxy object by calling -[Model mutableArrayValueForKey:@"events"].

NSMutableArray *events = [modelInstance mutableArrayValueForKey:@"events"];
[events addObject:newEvent];

This will trigger KVO notifications by setting the property with a new collection each time. For better performance and more granular control, implement the rest of the array accessors.

See also: Observing an NSMutableArray for insertion/removal.

这篇关于使用 KVO 与 NSNotificationCenter 观察对可变数组的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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