0

我目前正在为 iOS 重写一个表单控制器。它是一个绑定到模型的自定义对象,并处理编辑表单字段、跳转到上一个/下一个字段、处理自定义键盘、验证数据......

第一个版本基于用于存储表单值的 plist,表单控制器本身保存所有数据。现在我想将存储(模型)与表单控制器分离,因此我已经决定使用 KVO。

为简单起见,让我们假设我有一个设计用于编辑缺勤时间跨度的表单。所以它有两个字段:leaveDatereturnDate.

我的模型如下:

@interface Absence
    @property (strong, nonatomic) NSDate *leaveDate;
    @property (strong, nonatomic) NSDate *returnDate;
    @property (readonly, nonatomic) BOOL isValid;
@end

我的表单控制器有一个model指向该对象的属性。

当用户点击我的 XIB 中的“离开日期”文本字段时,表单控制器会根据我的模型的当前值提交并显示一个日期选择器leaveDate。当用户选择其他日期时,表单控制器使用setValue:forKey:.

isValid属性被声明为受leaveDateand returnDate(使用+keyPathsForValuesAffectingIsValid)影响,并且表单控制器已注册以监视此属性的更改,以动态启用/禁用提交按钮。

到目前为止,一切都像一个魅力。现在,对于扭曲的部分:

我希望我的表单控制器能够在模型打开时处理模型中的更改。示例:我在模型中有一条规则,即“缺勤必须至少持续 3 天”。当用户更改休假日期时,如果总时长不超过 3 天,则自动调整返回日期。

所以我的表单控制器还必须注册以监听所有属性的变化。问题是它既改变了属性,又监听了变化。

这样,当用户更改时leaveTime,表单控制器会使用setValue:forKey:更新模型,但会立即收到 KVO 通知,告知其刚刚所做的更改。这是不必要的并且可能有害(我只是自己进行了更改,不需要告诉我我刚刚完成了)。

到目前为止,我发现的唯一方法是在设置新值之前取消注册,然后立即重新注册,如下所示:

[self.model removeObserver:self forKeyPath:self.currentField.key];
[self.model setValue:newValue forKey:self.currentField.key];
[self.model addObserver:self forKeyPath:self.currentField.key options:NSKeyValueObservingOptionNew context:nil];

它正在工作,但它很丑陋,而且在性能方面我怀疑它是否很棒。

有人对如何做得更好有解释吗?

TL;博士

ControllerA是 的注册 KVO 观察员Model

ControllerB更新Model==>ControllerA收到 KVO 通知。没关系。

ControllerA更新Model==>ControllerA收到 KVO 通知。我不要这个。

4

1 回答 1

2

你似乎很关心性能。我不会的。绘图由主运行循环合并,因此设置textField.text = @"foo";不应导致绘图、图像处理等在线发生。通常,像这样的设置器会设置它的值,然后调用[self setNeedsDisplay]它只设置一个标志(非常便宜),然后在运行循环结束时,绘图系统将触发一次重绘。你可以设置textField.text一千次,仍然应该只有一次绘制操作。

正如评论者所建议的那样,您应该这样做,以便您的控制器能够容忍多次更新。如果您正在与 setter 一起进行大量工作,请不要这样做。二传手应该是“愚蠢的”。他们应该设置该值,并在必要时设置一个标志(如setNeedsDisplay)。在这种情况下,你应该避免在 setter 中做“真正的工作”。

正如另一位评论者所建议的那样,您也可以不费心在线更新 UI,而让 KVO 将更改传播给所有观察者,包括导致更改的控制器。

确实,这些方法中的任何一种都可以,但我怀疑您的性能问题是没有根据的。如果存在性能问题,问题不在于有多个更新,而在于您在每次更新期间都在做真正的工作,此时您应该设置一个标志并稍后再做工作。

于 2013-06-18T12:37:39.993 回答