需要一些关于Cocoa MVC / KVO模式的提示

这是一个非常广泛/模糊的问题,但这里有.提前道歉.

我正在构建的应用程序(桌面应用程序)采用不同类型的输入来生成QR代码(我只是构建它来学习一些Obj-C / Cocoa).用户可以在允许输入纯文本(单文本字段),VCard / MeCard数据(多个文本字段)和其他内容的不同视图之间切换.无论输入如何,结果都是QR码.

为了保持包含,我想将视图用作视图控制器,因此它们处理它们自己的输入,并且可以简单地将包含所有数据的通用“数据编码”对象“发送”到中央编码器.即纯文本视图将使用其文本字段的文本创建数据对象,而VCard / MeCard视图将使用其所有字段来生成结构化的VCard / MeCard数据.

我可以在代码中手动绑定所有这些东西,但我真的想学习bindings / KVO如何帮助我.唉,在阅读了Apple的开发人员文档,以及我能找到的更简单的教程/示例之后,我仍然不确定如何将它应用到我的应用程序中.

例如:用户在VCard视图中编辑文本字段.每次更新都会通知VCard视图控制器并“重新计算”数据对象.然后,中央编码器控制器被通知更新的数据对象,并对数据进行编码.

所有这一点,是输入视图可以完全独立创建,并且可以包含各种输入字段.然后,他们处理自己的输入,并“返回”编码器可以使用的通用数据对象.在内部,视图观察它们的输入以更新数据对象,并且外部编码器仅需要观察数据对象.

麻烦的是我不知道如何使这一切发生并保持解耦.输入视图和其字段之间是否应该有对象控制器?视图和编码器之间应该有另一个吗?我需要什么?如果有人有一个很好的教程链接,请分享.

同样,我可以推出自己的通知系统和粘合代码,但我认为重点是避免这种情况.

最佳答案 绝对是一个模糊的问题,但一个初学者到另一个,我觉得你的痛苦:)

我下载并解压缩每个示例并经常浏览它们.我发现让我超越驼峰是最有价值的事情.我绝对建议不要放弃这些例子.我砍掉了this script来下载并打开它们.

就良好的KVO模式而言,我发现技术described here非常有用.然而,在Objective-C 2.0中它是doesn’t work as-is.此外,他没有详细说明它是如何实际使用的.这就是我的工作:

KVODispatcher.h是这样的:

#import <Foundation/Foundation.h>

@interface KVODispatcher : NSObject {

    id owner;
}

@property (nonatomic, retain) id owner;

- (id) initWithOwner:(id)owner;

- (void)startObserving:(id)object keyPath:(NSString*)keyPath 
               options:(NSKeyValueObservingOptions)options 
              selector:(SEL)sel;

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context;
@end

KVODispatcher.m如下:

#import "KVODispatcher.h"
#import <objc/runtime.h>

@implementation KVODispatcher

@synthesize owner;

- (id)initWithOwner:(id)theOwner 
{
    self = [super init];
    if (self != nil) {
        self.owner = theOwner;
    }
    return self;
}

- (void)startObserving:(id)object 
               keyPath:(NSString*)keyPath 
               options:(NSKeyValueObservingOptions)options 
              selector:(SEL)sel
{
    // here is the actual KVO registration
    [object addObserver:self forKeyPath:keyPath options:options context:sel];
}

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context
{
    // The event is delegated back to the owner
    // It is assumed the method identified by the selector takes
    // three parameters 'keyPath:object:change:'
    objc_msgSend(owner, (SEL)context, keyPath, object, change);

    // As noted, a variation of this technique could be 
    // to expand the data passed in to 'initWithOwner' and 
    // have that data passed to the selected method here.
}
@end

然后你可以注册观察这样的事件:

KVODispatcher* dispatcher = [[KVODispatcher alloc] initWithOwner:self];
[dispatcher startObserving:theObject 
                   keyPath:@"thePath" 
                   options:NSKeyValueChangeNewKey 
                   selector:@selector(doSomething:object:change:)];

在执行上述操作的同一个对象中,您可以使用如下方法:

- (void) doSomething:(NSString *)keyPath 
             object:(id)object 
             change:(NSDictionary *)change {

    // do your thing
}

您可以根据需要使用这些“doSomething”类型的方法.只要他们使用相同的参数(keyPath:object:change :)就可以了.每个对象有一个调度程序,希望接收有关任意数量对象更改的任意数量的通知.

我喜欢它:

>每个类只能有一个observeValueForKeyPath,但您可能想要观察几件事.自然的下一个想法是“嘿,也许我可以通过一个选择器”
>哦,但是除非使用像NSNotification这样的包装器对象,否则不能通过performSelector传递多个参数.谁想要清理包装器对象.
>当超类也使用KVO时,覆盖observeValueForKeyPath会使任何通用方法变得困难 – 您必须知道哪些通知要传递给超类以及要保留哪些通知.
>谁想要在每个对象中重新实现相同的基于泛型选择器的observeValueForKeyPath?最好只做一次并重复使用它.

一个不错的变化可能是将另一个字段(如id additionalContext)添加到KVODispatcher,并在objc_msgSend调用中传递该additionalContext对象.使用它来隐藏在观察到的数据发生变化时需要更新的UI对象会很有用.甚至可能是NSArray.

点赞