13

我想在一個對象的多個事件(1對N關係)上有多個觀察者。何時使用NSNotificationCenter

實現此任務的機制由NSNotificationCenter提供。該機制看起來相當矯枉過正時用於我的問題。

我怎麼會做手工,而無需使用NSNotificationCenter

- (void)addDelegate:(id<DelegateProtocol>)delegate; 
- (void)removeDelegate:(id<DelegateProtocol>)delegate; 

從我的對象添加和刪除觀察員。

- (void)someEventFired:(NSObject<NSCopying> *)eventData 
{ 
    for (id delegate in delegates) { 
     NSObject *data = [eventData copy]; 
     [delegate someEventFired:data]; 
    } 
} 

這種機制是直接的,簡單的,而無需額外的共享字符串對象來實現。

  • 除了NSNotificationCenter之外,在iOS框架中是否存在1對N代表(如C#事件)的官方模式?
  • 什麼時候應該使用NSNotificationCenter?何時不使用?
  • 什麼時候應該使用一個像我在這裏建議的實現,而不是什麼時候使用?
+0

我很少或甚至沒有使用NSNotificationCenter,但是採用了與您描述的相同的方法。我已經在很多iOS應用中使用過它(我會說超過50個應用)很多年了,至今我還沒有看到任何問題。一個可能的問題可能是您需要確保有時會刪除觀察員,或者他們可能不會在預期時被釋放,因爲他們由代表的守護者保留。 – Jonny

+0

我認爲即使NSNotificationCenter不是我們從C#知道的方法,我們仍然應該專注於使用我們工作的特定平臺的設計模式。也就是說,我現在已經在幾個應用程序中使用了NSNotificationCenter(儘管如此),並且我認爲代碼通過它變得更糟。 – Etan

回答

14

按照慣例,委託人應該只能用於1:1的關係。如果您確實需要1:N關係進行此類功能,您有兩種選擇:

  1. 如您所述,NSNotificationCenter
  2. Key-Value Observing(也被稱爲KVO)。

如果您只關心對象的特定屬性何時更改,則KVO是適當的。否則,你應該只考慮使用NSNotificationCenter。只有當特定對象通過將該對象傳遞給addObserver:selector:name:object:方法來發布通知時,您甚至可以收到通知。

蘋果使用在類似的情形NSNotification(像UITextField定義的通知,包括UITextFieldTextDidBeginEditingNotificationUITextFieldTextDidChangeNotification,和UITextFieldTextDidEndEditingNotification)。

+0

你能告訴我,如果我正確使用它嗎? http://stackoverflow.com/questions/42111829/share-object-state-between-view-controllers-in-ios-development/42113389#42113389 –

-3

我說NSNotificationCenter應該總是在委託模型中使用,除非您在信息上查詢委託(例如-webView:shouldLoadRequest:)。它更穩定,更容易實現,並導致更乾淨的代碼,然後嘗試使用委託。另一種選擇是塊,這可能是好的,但在記憶管理方面它們可能會很痛苦。最後,這取決於你,但我認爲,NSNotificationCenter是幾乎所有情況下最好的方法,只要有多個觀察者功能即可。

1

NSNotificationCenter對於您的建議並不過分,它確實是正確的解決方案。它可以防止觀察對象不得不知道或關心其觀察者,從而使您的代碼更加鬆散耦合和更清晰。

爲通知名稱共享字符串並不重要,如果您的觀察者需要導入此頭文件以執行其作業,則可以在共享常量文件中或在觀察對象的頭文件中定義它們。

0

1對N委託關係沒有意義。看看

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 

例如。如果這個對象真的有n個代表呢?它應該如何決定從其所有代表中獲得的n個視圖中的哪一個應該被使用?代表們正是這種1比1的原則。

NSNotificationCenter是正確的方法。只需使用

addObserver:selector:name:object: 

分別

postNotification: 

這絕對不是太多的代碼。中心處理所有呼叫對您來說非常簡單。

2

使用通知是廣播:1發件人只是發送一個信息,誰曾經收到,接收它。小資很像廣播電臺,沒有頻道(讓我們暫時忘掉電話)

代表團是不同的。這個對象,要求刪除某件事,通常需要該請求的結果,因此,委託是一對一的通信,始終由對象啓動,而不是委託(而對象可以有方法可以被調用以通知對象發起通信,即[tableView reloadData])。

所以如果發件人需要取回數據,那就是委派。如果發件人在廣播後不關心任何內容,請使用通知。

如果遇到這種情況,您需要委派,但有幾個對象應該實現協議。你應該有一個委託,它保存對其他對象的引用並代表發送者調用方法 - 或者你可以使用塊。

+0

今天,我只會有一個對象,所有風險投資共享,並通知他們。 – vikingosegundo

1

您提出的解決方案既不比使用NSNotificationCenter更簡單,也不是線程安全的。

爲了讓您的解決方案線程安全,您需要提供一種機制,以防止在運行事件派發for循環時委派數組發生更改。

您的解決方案還要求您在類中維護委託數組。藉助NotificationCenter,您可以簡單地使用默認中心,而無需在課堂中實施添加/刪除方法。相反,實例可以註冊自己接收通知,因爲他們認爲最合適(選擇器/塊,隊列,源)。您的源類不必擔心這些細節。它只需要將自己註冊爲指定類型通知的來源。使用塊來處理通知非常方便。

通知中心的另一種選擇是使用Key-Value-Observing(如果符合您的用例的需要)。

最終,您決定使用的機制取決於它如何最好地適用於您的特定用例。

0

您不希望將NSNotificationCenter用於系統範圍事件以外的其他任何事情(例如鍵盤或某些類似事件的出現)。原因在於它完全不是類型安全的,可以使所有的東西都依賴於所有東西,並且不再需要編譯時間檢查或使用搜索結果。

KVO在我看來不應該用來觀察你正在聽的對象以外的變化,因爲它有類似的下方(沒有編譯時間檢查,崩潰,如果你不正確地移除監聽器或註冊它們兩次) 。

您提出的addDelegate/removeDelegate模式在我看來是完全正確的路徑,因爲它具有維護類型安全性和編譯器檢查的優點,並使依賴性顯式化。唯一的問題是,Apple不提供這種模式的開箱即用解決方案,因爲您需要一個集合類型,它可以保留其元素以避免保留週期。

但是,請參閱我的BMCommons framework中的代碼,它使用BMNullableArray和宏整齊地解決了此問題。見BMCore.h標題爲那些宏的定義:

BM_LISTENER_METHOD_DECLARATION(protocol) 
BM_LISTENER_METHOD_IMPLEMENTATION(protocol) 

的實施確保了相同的偵聽器將永遠不會被添加兩次,也是聽衆弱保留,不會造成即使他們忘記註銷自己在任何崩潰取消分配(雖然我更喜歡通過斷言來捕捉這個條件,因爲這是一個編程錯誤)。