我目前正在为iOS重写表单控制器。这是一个绑定到模型的自定义对象,并处理编辑表单字段,跳到上一个/下一个字段,处理自定义键盘,验证数据...双向KVO:控制器更新模型,它通知控制器
第一个版本基于plist来存储表单值,表单控制器保存所有数据本身。现在我想从窗体控制器中分离存储(模型),因此我已经使用KVO进行了解决。
为了简单起见,我们假设我有一个旨在编辑缺席时间跨度的表单。所以它有两个字段:leaveDate
和returnDate
。
我的模型是如下:
@interface Absence
@property (strong, nonatomic) NSDate *leaveDate;
@property (strong, nonatomic) NSDate *returnDate;
@property (readonly, nonatomic) BOOL isValid;
@end
我的形式控制器具有属性model
它指向该对象。
当用户点击我的XIB中的“离开日期”文本字段时,表单控制器会根据我的模型的当前值leaveDate
提交日期选择器。当用户选择其他日期时,表单控制器将使用setValue:forKey:
更新其模型。
的isValid
属性被声明为leaveDate
和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; DR
ControllerA
是Model
注册KVO观察者。
ControllerB
更新Model
==>ControllerA
收到KVO通知。没关系。
ControllerA
更新Model
==>ControllerA
收到KVO通知。我不想要这个。
刚刚听到KVO通知,你刚刚做出了什么改变?我认为这种选择比删除并重新添加自己作为KVO观察员更“不适合并且有潜在危害”。 – occulus 2013-02-14 16:02:06
我不想执行'textField.text = @“something”;'当我在更新模型之前做的最后一件事情是'textField.text = @“something”;'。想象一下,这是一个更经济的用户界面更新(重新绘制大型控件,图像处理...),经常发生(离散控制,如滑块)。 – Cyrille 2013-02-14 16:03:29
如果您确定接收通知是有效的幂等操作,则多次接收相同的通知无关紧要。 – occulus 2013-02-14 16:03:59