Cocoa绑定和KVO
我有一个视图Cocoa绑定和KVO,cocoa,cocoa-bindings,key-value-observing,Cocoa,Cocoa Bindings,Key Value Observing,我有一个视图MyView,它有一些图像,我想用我的AppDelegate中的数组绑定它们 MyViewclass @interface MyView : NSView { @private NSArray *images; } @end + (void)initialize { [self exposeBinding:@"images"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)o
MyView
,它有一些图像,我想用我的AppDelegate
中的数组绑定它们
MyView
class
@interface MyView : NSView {
@private
NSArray *images;
}
@end
+ (void)initialize
{
[self exposeBinding:@"images"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"Changed!");
}
我的AppDelegate
@property (retain) NSArray *images;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
images = [[NSMutableArray alloc] init];
[view bind:@"images" toObject:self withKeyPath:@"images" options:nil];
// [self addObserver:view forKeyPath:@"images" options:0 context:nil]; // !!!
MyImage *img = [[MyImage alloc] ...];
[self willChangeValueForKey:@"images"];
[[self images] addObject:img];
[self didChangeValueForKey:@"images"];
[img release];
}
不带[self addObserver:view forKeyPath:@“images”选项:0上下文:nil]代码>永远不会调用方法observeValueForKeyPath:
使用bind:
时是否需要调用addObserver:
?bind:
是否设置KVO?为什么绑定不起作用呢?调用observeValueForKeyPath
的唯一方法是调用addObserver
。绑定通过一种不同的机制工作。您需要的是一个针对images属性的实现的setter,如下所示。最常见的使用情况是,您需要使图形无效,并请求使用重新绘制
-设置需要显示:是
- (void)setImages:(NSArray *)newImages
{
if(newImages != images) {
[images release];
images = newImages;
[images retain];
}
[self setNeedsDisplay:YES]; // Addition and only difference to synthesized setter
}
您可以删除-exposeBinding:
调用,因为这只会影响Interface Builder的插件,以及随着Xcode 4的引入而丢失的插件
-observeValueForKeyPath:of对象:更改:上下文:
消息未发送的原因是,对于绑定,观察者不是绑定到对象的对象。背景中还有另一个物体。(在堆栈形式的断点中,您可以看到它的类是NSEditableBinder。)因此,从视图中注册为观测者是正确的,可以将视图属性@“images”注册为观测者
另一种获得视图更改通知的方法是重写-setValue:forKey:
方法。然后需要检查键字符串,看看它是否等于@“images”
。但由于KVC协议还有其他方法,如-setValue:forKeyPath:
,因此您需要格外小心,以免干扰机器,即始终调用super
嗯。我刚刚意识到,到目前为止,我的答案假设替换整个阵列的情况更简单。你的问题是要修改数组。(不过,在您的示例中,您确实声明了一个不可变的数组属性,它只允许替换。因此,请保留声明的属性,我的方法到目前为止仍然有效。下面我将展示另一种替代方法。)
好的,假设您在app delegate中执行此操作,替换:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[view bind:@"images" toObject:self withKeyPath:@"images" options:nil];
MyImage *img = [[MyImage alloc] ...];
self.images = [NSArray arrayWithObject:img];
[img release];
}
您不需要发布更改(使用willChangeValueForKey:
和didChangeValueForKey:
),因为您需要查看声明的属性。他们会为您这样做
现在转到修改数组的另一种方法。为此,需要使用可变数组属性,并通过KVO通知代理对其进行修改,如下所示:
[self-mutableArrayValueForKey:@“images”]addObject:img];
这将在发送(绑定到)端获取更改,然后通过绑定机制将其传输到视图,并最终使用KVC进行设置
在那里,在视图的接收端,您需要获取对@“images”的属性更改。这可以通过覆盖集合访问器方法并在那里做更多的工作来完成,而不仅仅是接受更改。但是这有点复杂,因为有很多访问器方法(请参阅)。或者,更简单地说,您可以从视图中添加另一个观察关系
为此,在视图的初始化(-awakeFromNib:
等)中的某个地方:
[self addObserver:self forKeyPath:@"images" options:0 context:nil];
然后:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
if([keyPath isEqualToString:@"images"]) {
[self setNeedsDisplay:YES]; // or what else you need to do then.
}
}
请注意,这最后一个观察者关系与绑定不再有任何关系。绑定属性的值更改正确地到达视图,而您只是没有意识到(得到通知)
这应该是可行的。没有机制,它只是为每个支持绑定的视图的每个绑定单独编码。这不是在自定义视图中可以免费获得的。如果不编写自定义实现,就不能使用exposeBinding:和bind:。有一些示例说明了如何做到这一点(但并不容易)这个答案不准确。绑定通过相同的机制工作,即使用KVO拾取更改,使用KVC将更改设置为绑定对象。绑定对象没有执行观察(KVO)但是,这就是为什么不调用-observer-value:forKeyPath:change:
的原因。当您中断绑定setter方法时,您将发现涉及NSEditableBinder
实例(但这似乎是未记录的API)。