簡體   English   中英

選擇器或塊用於Objective-C庫中的回調

[英]Selectors or Blocks for callbacks in an Objective-C library

我們正在Objective-C中開發一個自定義的EventEmitter靈感消息系統。 對於聽眾提供回調,我們是否需要選擇器以及為什么?

作為開發人員,您更願意使用哪個第三方庫? 哪個最符合Apple的軌跡,指導方針和做法?

背景

我們正在使用Objective-C開發一個全新的iOS SDK,其他第三方將使用它來將功能嵌入到他們的應用程序中。 SDK的很大一部分需要向偵聽器傳遞事件。

我知道在Objective-C中做回調有五種模式,其中三種不適合:

  • NSNotificationCenter - 無法使用,因為它不保證訂單觀察者將被通知,並且因為觀察者無法阻止其他觀察者接收事件(如JavaScript中的stopPropagation() )。
  • 鍵值觀察 - 似乎不是一個很好的架構適合,因為我們真正擁有的是消息傳遞,而不是總是“狀態”約束。
  • 代表和數據源 - 在我們的例子中,通常會有許多聽眾,而不是一個可以正確稱為代表的聽眾。

其中兩個是競爭者:

  • 選擇器 - 在此模型下,調用者提供一個選擇器和一個目標,它們被共同調用以處理事件。
  • - 在iOS 4中引入,塊允許傳遞功能而不綁定到像觀察者/選擇器模式這樣的對象。

這可能看起來像一個深奧的意見問題,但我覺得有一個客觀的“正確”的答案,我只是在Objective-C中缺乏經驗來確定。 如果這個問題有一個更好的StackExchange站點,請幫助我將其移動到那里。

更新#1 - 2013年4月

我們選擇作為為事件處理程序指定回調的方法。 我們對此選擇感到非常滿意,並且不打算刪除基於塊的偵聽器支持。 它確實有兩個值得注意的缺點:內存管理和設計阻抗。

內存管理

塊最容易在堆棧上使用。 通過將它們復制到堆上來創建長壽命塊會引入有趣的內存管理問題。

調用包含對象上的方法的塊會隱式地提升self的引用計數。 假設你有一個類的name屬性的setter,如果你在一個塊中調用name = @"foo" ,編譯器將其視為[self setName:@"foo"]並保留self以便它不會當塊仍在周圍時解除分配。

實現EventEmitter意味着擁有長期存在的塊。 為了防止隱性保留,發射器的用戶需要創建一個__block參考self塊,前外:

__block *YourClass this = self;
[emitter on:@"eventName" callBlock:...
   [this setName:@"foo"];...
}];

這種方法唯一的問題是, this被調用的處理之前,可能被釋放。 因此,用戶必須在取消分配時取消注冊其偵聽器。

設計阻抗

經驗豐富的Objective-C開發人員希望使用熟悉的模式與庫進行交互。 代表是一種非常熟悉的模式,因此規范的開發人員希望使用它。

幸運的是,委托模式和基於塊的偵聽器不是互斥的。 雖然我們的發射器必須能夠處理來自許多地方的偵聽器(只有一個委托不起作用),但我們仍然可以公開一個接口,允許開發人員與發射器進行交互,就像它們的類是委托一樣。

我們還沒有實現這一點,但我們可能會根據用戶的請求。

更新#2 - 2013年10月

我不再致力於產生這個問題的項目,我很高興地回到了我原生的JavaScript之鄉。

接管這個項目的智能開發人員正確地決定完全退出我們的基於塊的自定義EventEmitter。 即將發布的版本已切換到ReactiveCocoa

這使得它們比先前提供的EventEmitter庫具有更高級別的信令模式,並且允許它們比基於塊的事件處理程序或類級方法更好地封裝信號處理程序內的狀態。

就個人而言,我討厭使用代表。 由於Objective-C的結構如何,它真的使代碼混亂如果我必須創建一個單獨的對象/添加協議只是為了通知你的一個事件,我必須實現5/6。 出於這個原因,我更喜歡積木。

雖然他們(塊)確實有它們的缺點(ex內存管理可能很棘手)。 它們易於擴展,易於實現,並且在大多數情況下才有意義

雖然apple的設計結構可能使用sender-delegate方法,但這只是為了向后兼容。 最近的Apple API一直在使用塊(來自CoreData),因為它們是objective-c的未來。 雖然它們在使用時可能會使代碼混亂,但它也允許更簡單的“匿名代表”,這在目標C中是不可能的。

最后,它實際上歸結為:你是否願意放棄一些較舊的,更加過時的平台來換取使用塊與代表? 委托的一個主要優點是它可以保證在任何版本的objc-runtime中工作,而塊是該語言的最新版本。

NSNotificationCenter / KVO而言,它們既有用又有其目的,但作為代表,它們並不打算被使用。 也不能將結果發送回發件人,在某些情況下,這是至關重要的(例如, -webView:shouldLoadRequest: )。

我認為正確的做法是實現兩者,將其用作客戶端,並看看哪種感覺最自然。 這兩種方法都有優勢,它實際上取決於上下文以及您希望如何使用SDK。

選擇器的主要優點是簡單的內存管理 - 只要客戶端正確注冊和取消注冊,就不必擔心內存泄漏。 使用塊,內存管理可能會變得復雜,具體取決於客戶端在塊內執行的操作。 單元測試回調方法也更容易。 塊當然可以寫成可測試的 ,但是從我看到的內容來看並不常見。

塊的主要優點是靈活性 - 客戶端可以輕松引用局部變量而無需使用ivars。

所以我認為這僅僅取決於用例 - 這樣的一般設計問題沒有“客觀正確答案”。

很棒的寫作!

在個人觀點中,來自編寫大量JavaScript的事件驅動編程比讓代表來回更清晰。

關於偵聽器的內存管理方面,我嘗試解決這個問題(大量來自Mike Ash的MAKVONotificationCenter ),調整了調用者和發射器的dealloc實現( 如此處所示 ),以便以兩種方式安全地刪除偵聽器。

我不完全確定這種方法有多安全,但我們的想法是嘗試它直到它破裂。

關於圖書館的一件事是,您只能在某種程度上預期,如何使用它。 所以你需要提供一個盡可能簡單和開放的解決方案 - 並且對用戶來說很熟悉。

  • 對我來說,這一切最適合代表團。 雖然你是對的,它只能在偵聽器(委托)上,這意味着沒有限制,因為用戶可以將一個類編寫為委托,它知道所有想要的偵聽器並通知它們。 當然,您可以提供注冊課程。 將在所有已注冊的對象上調用委托方法。
  • 積木也一樣好。
  • 你所說的選擇器被稱為目標/動作,簡單但功能強大。
  • KVO似乎對我來說似乎不是最佳解決方案,因為它可能會削弱封裝,或導致使用庫類的wrog心理模型。
  • NSNotifications很好地告知某些事件,但不應強迫用戶使用它們,因為它們非常非正式。 如果有人調到,你的課程將無法知道。

關於API設計的一些有用的想法: http//mattgemmell.com/2012/05/24/api-design/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM