0

現在、iOS 用のフォーム コントローラーを書き直しています。モデルにバインドされたカスタム オブジェクトであり、フォーム フィールドの編集、前/次のフィールドへのジャンプ、カスタム キーボードの処理、データの検証などを処理します。

最初のバージョンは、フォーム値を格納するための plist に基づいており、フォーム コントローラーはすべてのデータ自体を保持していました。ここで、ストレージ (モデル) をフォームコントローラーから切り離したいので、KVO を使用することにしました。

簡単にするために、不在の期間を編集するように設計されたフォームがあると仮定しましょう。したがって、 と の 2 つのフィールドが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影響を受けると宣言され、フォーム コントローラーはこのプロパティの変更を監視するために登録され、その場で送信ボタンを有効/無効にします。leaveDatereturnDate+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の登録済み KVO オブザーバーですModel

ControllerB更新Model==> ControllerAKVO 通知を受け取ります。それはいいです。

ControllerA更新Model==> ControllerAKVO 通知を受け取ります。これはいらない。

4

1 に答える 1

2

パフォーマンスを気にしているようです。私はそうではないでしょう。描画はメインの実行ループによって結合されるため、設定によってtextField.text = @"foo";描画や画像処理などがインラインで発生することはありません。通常、そのようなセッターはその値を設定してから[self setNeedsDisplay]、フラグを設定するだけの呼び出しを行い (非常に安価です)、その後、実行ループの最後に、描画システムが単一の再描画をトリガーします。textField.text何千回も設定できますが、描画操作は 1 回だけです。

コメンターが示唆しているように、コントローラーが複数の更新に耐えられるようにする必要があります。セッターとインラインで大量の作業を行っている場合は、そうしないでください。セッターは「ばか」であるべきです。値を設定し、必要に応じてフラグを設定する必要があります ( などsetNeedsDisplay)。このような状況では、セッターで「実際の作業」を行うことは避けるべきです。

別のコメンターが示唆したように、UI をインラインで更新することを気にせず、KVO が変更を引き起こしたコントローラーを含むすべてのオブザーバーに変更を波及させることもできます。

実際、これらのアプローチはどれも機能しますが、パフォーマンスに関する懸念には根拠がないと思われます。パフォーマンスの問題がある場合、問題は複数の更新があることではなく、フラグを設定して後で作業を行う必要があるときに、各更新中に実際の作業を行っていることです。

于 2013-06-18T12:37:39.993 に答える