2013-02-14 98 views
0

我目前正在爲iOS重寫表單控制器。這是一個綁定到模型的自定義對象,並處理編輯表單字段,跳到上一個/下一個字段,處理自定義鍵盤,驗證數據...雙向KVO:控制器更新模型,它通知控制器

第一個版本基於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屬性被聲明爲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

ControllerAModel註冊KVO觀察者。

ControllerB更新Model ==>ControllerA收到KVO通知。沒關係。

ControllerA更新Model ==>ControllerA收到KVO通知。我不想要這個。

+0

剛剛聽到KVO通知,你剛剛做出了什麼改變?我認爲這種選擇比刪除並重新添加自己作爲KVO觀察員更「不適合並且有潛在危害」。 – occulus 2013-02-14 16:02:06

+0

我不想執行'textField.text = @「something」;'當我在更新模型之前做的最後一件事情是'textField.text = @「something」;'。想象一下,這是一個更經濟的用戶界面更新(重新繪製大型控件,圖像處理...),經常發生(離散控制,如滑塊)。 – Cyrille 2013-02-14 16:03:29

+0

如果您確定接收通知是有效的冪等操作,則多次接收相同的通知無關緊要。 – occulus 2013-02-14 16:03:59

回答

2

您似乎對性能感到擔憂。我不會。繪圖由主運行循環合併,所以設置textField.text = @"foo";不應導致繪圖,圖像處理等在線發生。通常情況下,像這樣的setter會設置它的值,然後調用[self setNeedsDisplay],它只設置一個標誌(非常便宜),然後在運行循環結束時,繪圖系統將觸發單個重繪。您可以設置textField.text一千次,而且應該只有一次抽獎操作。

正如評論者所建議的,您應該讓它成爲您的控制器可以容忍多個更新。如果你正在做一系列的工作,那就不要這樣做。安裝者應該是「愚蠢的」。他們應該設置該值,並在必要時設置標誌(如setNeedsDisplay)。在這樣的情況下,你應該避免在制定者中做「真正的工作」。

正如一位網民建議,你也可能只是懶得更新在線的用戶界面,讓志願波及了改變所有的觀察員,包括引起的變化,則控制器。

真的,任何這些方法都行得通,但我懷疑你的表現擔憂是沒有根據的。如果出現性能問題,問題不在於有多個更新,而在於每次更新期間您都在進行實際工作,此時您應該設置一個標記並在稍後進行工作。