关于模型视图控制器:需要一些关于 Cocoa MVC/KVO 模式的提示

Need some tips regarding the Cocoa MVC/KVO patterns

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

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

为了控制内容,我希望将视图用作视图控制器,以便它们处理自己的输入,并且可以简单地"发送"一个通用的"数据编码"对象包含所有数据到中央编码器。 IE。纯文本视图将使用其文本字段的文本生成数据对象,而 VCard/MeCard 视图将使用其所有字段来生成结构化 VCard/MeCard 数据。

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

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

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

问题是我不知道如何让这一切发生并保持解耦。输入视图和它的字段之间应该有一个对象控制器吗?视图和编码器之间应该有另一个吗?我在哪里需要什么?如果有人有一个好的教程的链接,请分享。

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


绝对是一个模糊的问题,但一个初学者对另一个,我感到你的痛苦:)

我下载并解压了每个示例,并经常使用 grep 搜索它们。我发现这是让我度过难关的最有价值的事情。我绝对建议不要放弃这些例子。我破解了这个脚本来下载并解压它们。

就良好的 KVO 模式而言,我发现这里描述的技术非常有用。但是,它在 Objective-C 2.0 中不能按原样工作。他也没有详细说明它的实际使用方式。这是我的工作:

KVODispatcher.h 是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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 也是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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

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

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

在执行上述操作的同一个对象中,你可以有这样的方法:

1
2
3
4
5
6
- (void) doSomething:(NSString *)keyPath
             object:(id)object
             change:(NSDictionary *)change {

    // do your thing
}

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

我喜欢它的地方:

  • 每个类只能有一个 observeValueForKeyPath,但您可能想观察几件事。自然的下一个想法是"嘿,也许我可以通过选择器"
  • 哦,但是不可能通过 performSelector 传递多个参数,除非使用像 NSNotification 这样的package对象。谁想清理package对象。
  • 当超类也使用 KVO 时覆盖 observeValueForKeyPath 会使任何通用方法都变得困难——您必须知道哪些通知要传递给超类以及要保留哪些通知。
  • 无论如何,谁想在每个对象中重新实现相同的基于通用选择器的 observeValueForKeyPath ?最好只做一次并重复使用它。
  • 一个不错的变化可能是将另一个字段(如 id additionalContext)添加到 KVODispatcher,并在 objc_msgSend 调用中传递该附加上下文对象。使用它来存储需要在观察到的数据更改时更新的 UI 对象可能很有用。甚至可能是一个 NSArray.