成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

探索iOS開發中的消息傳遞機制

移動開發 iOS
每個應用程序或多或少,都由一些松耦合的對象構成,這些對象彼此之間要想很好的完成任務,就需要進行消息傳遞。本文將介紹所有可用的消息傳遞機制,并通過 示例來介紹這些機制在蘋果的Framework中如何使用,同時,還介紹了一些最佳實踐建議,告訴你什么時機該選擇使用什么機制。

注1:本文由破船譯自Communication Patterns。

每個應用程序或多或少,都由一些松耦合的對象構成,這些對象彼此之間要想很好的完成任務,就需要進行消息傳遞。本文將介紹所有可用的消息傳遞機制,并通過 示例來介紹這些機制在蘋果的Framework中如何使用,同時,還介紹了一些***實踐建議,告訴你什么時機該選擇使用什么機制。

[[109437]]

雖然這一期的主題是關于Foundation Framework的,不過本文中還介紹了一些超出Foundation Framework(KVO和Notification)范圍的一些消息傳遞機制,另外還介紹了delegation,block和target- action。

大多數情況下,消息傳遞該使用什么機制,是很明確的了,當然了,在某些情況下該使用什么機制并沒有明確的答案,需要你親自去嘗試一下。

本文中,會經常提及接收者[recipient]發送者[sender]。在消息傳遞 機制中具體是什么意思,我們可以通過一個示例來解釋:一個table view是發送者,而它的delegate就是接收者。Core Data managed object context是notification的發送者,而獲取這些notification的主體則是接收者。一個滑塊(slider)是action消息 的發送者,而在代碼里面對應著實現這個action的responder就是接收者。對象中的某個屬性支持KVO,那么誰修改這個值,誰就是發送者,對應 的觀察者(observer)則是接收者。

可用的機制

首先我們來看看每種機制的具體特點。在下一節中,我會結合一個流程圖來介紹如何在具體情況下,選擇正確的消息傳遞機制。***,將介紹一些來自蘋果Framework中的示例,并會解釋在某種確定情況下為什么要選擇固定的機制。

KVO

KVO提供了這樣一種機制:當對象中的某個屬性值發生了改變,可以對這些值的觀察者做出通知。KVO的實現包含在Foundation里面,基于 Foundation構建的許多Framework對KVO都有所依賴。要想了解更多關于如何使用KVO,可以閱讀本期由Daniel寫的的KVO和KVC文章。

如果對某個對象中值的改變情況感興趣,那么可以使用KVO消息傳遞機制。這里有兩個要求,首先,接收者(會接收到值發生改變的消息)必須知道發送者 (值將發生改變的那個對象)。另外,接收者同樣還需要知道發送者的生命周期,因為在銷毀發送者對象之前,需要取消觀察者的注冊。如果這兩個要求都滿足了, 消息傳遞過程中可以是1對多(多個觀察者可以注冊某個對象中的值)。

如果計劃在Core Data對象上使用KVO,需要知道這跟一般的KVO使用方法有點不同。那就是必須結合Core Data的故障機制(faulting mechanism),一旦core data出現了故障,它將會觸發其屬性對應的觀察者(即使這些屬性值沒有發生改變)。

Notification

在不相關的兩部分代碼中要想進行消息傳遞,通知(notifacation)是非常好的一種機制,它可以對消息進行廣播。特別是想要傳遞豐富的信息,并且不一定指望有誰對此消息關心。

通知可以用來發送任意的消息,甚至包含一個userInfo字典,或者是NSNotifacation的一個子類。通知的獨特之處就在于發送者和接 收者雙方并不需要相互知道。這樣就可以在非常松耦合的模塊間進行消息的傳遞。記住,這種消息傳遞機制是單向的,作為接收者是不可以回復消息的。

delegation

在蘋果的Framework中,delegation模式被廣泛的只用著。delegation允許我們定制某個對象的行為,并且可以收到某些確定 的事件。為了使用delegation模式,消息的發送者需要知道消息的接收者(delegate),反過來就不用了。這里的發送者和接收者是比較松耦合 的,因為發送者只知道它的delegate是遵循某個特定的協議。

delegate協議可以定義任意的方法,因此你可以準確的定義出你所需要的類型。你可以用函數參數的形式來處理消息內容,delegate還可以 通過返回值的形式給發送者做出回應。如果只需要在相對接近的兩個模塊之間進行消息傳遞,那么Delegation是一種非常靈活和直接方式。

不過,過渡使用delegation也有一定的風險,如果兩個對象的耦合程度比較緊密,相互之間不能獨立存在,那么此時就沒有必要使用 delegate協議了,針對這種情況,對象之間可以知道相互間的類型,進而直接進行消息傳遞。例如UICollectionViewLayout和 NSURLSessionConfiguration。

block

Block相對來說,是一種比較新的技術,它***出現是在OS X 10.6和iOS 4中。一般情況下,block可以滿足用delegation實現的消息傳遞機制。不過這兩種機制都有各自的需求和優勢。

當不考慮使用block時,一般主要是考慮到block極易引起retain環。如果發送者需要reatain block,而又不能確保這個引用什么時候被nil,這樣就會發生潛在的retain環。

假設我們想要實現一個table view,使用block替代delegate,來當做selection的回調,如下:

  1. self.myTableView.selectionHandler = ^void(NSIndexPath *selectedIndexPath) { 
  2.     // handle selection ... 
  3. }; 

上面代碼的問題在于self retain了table view,而table view為了之后能夠使用block,進而 retain了block。而table view又不能把這個引用nil掉,因為它不知道什么時候不在需要這個block了。如果我們保證不了可以打破這個retain環,而我們又需要 retain發送者,此時block不是好的選擇。

NSOperation就可以很好的使用block,因為它能再某個時機打破retain環:

  1. self.queue = [[NSOperationQueue alloc] init]; 
  2. MyOperation *operation = [[MyOperation alloc] init]; 
  3. operation.completionBlock = ^{ 
  4.     [self finishedOperation]; 
  5. }; 
  6. [self.queue addOperation:operation]; 

乍一看這似乎是一個retain環:self retain了queue,queue retain了operation,而operation retain了completion block,而completion blockretain了self。不過,在這里,將operation添加到queue時,會使operation在某個時機被執行,然后從queue 中remove掉(如果沒有被執行,就會有大問題了)。一單queue移除了operation之后,retain環就被打破了。

再來一個示例:這里實現了一個視頻編碼器的類,里面有一個名為encodeWithCompletionHandler:的方法。為了避免出現retain環,我們需要確保編碼器這個對象能夠在某個時機nil掉其對block的引用。其內部代碼如下所示:

  1. @interface Encoder () 
  2. @property (nonatomic, copy) void (^completionHandler)(); 
  3. @end 
  4.  
  5. @implementation Encoder 
  6.  
  7. - (void)encodeWithCompletionHandler:(void (^)())handler 
  8.     self.completionHandler = handler; 
  9.     // do the asynchronous processing... 
  10.  
  11. // This one will be called once the job is done 
  12. - (void)finishedEncoding 
  13.     self.completionHandler(); 
  14.     self.completionHandler = nil; // <- Don't forget this! 
  15.  
  16. @end 

在上面的代碼中,一旦編碼任務完成,就會調用complietion block,進而把引用nil掉。

如果我們發送的消息屬于一次性的(具體到某個方法的調用),由于這樣可以打破潛在的retain環,那么使用block是非常不錯的選擇。另外,如 果為了讓代碼可讀性更強,更有連貫性,那***是使用block了。根據這個思路,block經??梢杂糜赾ompletion handler、error handler等。

Target-Action

Target-Action主要被用于響應用戶界面事件時所需要傳遞的消息中。iOS中的UIControl和Mac中的 NSControl/NSCell都支持這種機制。Target-Action在消息的發送者和接收者之間建立了一個非常松散耦合。消息的接收者不知道發 送者,甚至消息的發送者不需要預先知道消息的接收者。如果target是nil,action會在響應鏈(responder chain)中被傳遞,知道找到某個能夠響應該aciton的對象。在iOS中,每個控件都能關聯多個target-action。

基于target-action消息傳遞的機制有一個局限就是發送的消息不能攜帶自定義的payload。在Mac的action方法中,接收者總 是被放在***個參數中。而在iOS中,可以選擇性的將發送者和和觸發action的事件作為參數。除此之外,沒有別的辦法可以對發送action消息內容 做控制。

做出正確的選擇

根據上面討論的結果,這里我畫了一個流程圖,來幫助我們何時使用什么消息傳遞機制做出更好的決定。忠告:流程圖中的建議并非最終的答案;可能還有別的選項依然能實現目的。只不過大多數情況下此圖可以引導你做出正確的決定。

上圖中,還有一些細節需要做更近一步的解釋:

上圖中的有個盒子這樣說到:sender is KVO compliant(發送者支持compliant)。這不僅以意味著當值發生改變時,發送者會發送KVO通知,并且觀察者還需要知道發送者的生命周期。 如果發送者被存儲在一個weak屬性中,那么發送者有可能被nil掉,進而引起觀察者發生leak。

另外底部的一個盒子說到:message is direct response to method call(消息直接在方法的調用代碼中響應)。也就是說處理消息的代碼跟方法的調用代碼處于相同的地方。

***,在左下角,處于一個決策問題的判斷狀態:sender can guarantee to nil out reference to block?(發送者能夠確保nil掉到block的引用嗎?),這實際上涉及到之前我們討論到基于block 的APIs已經潛在的retain環。使用block時,如果發送者不能保證在某個實際能夠把對block的引用nil掉,那么將會遇到retain環的 問題。

Framework示例

本節我們通過一些來自蘋果Framework的示例,來看看在實際使用某種機制之前,蘋果是處于何種原因做出選擇的。

KVO

NSOperationQueue就是lion給了KVO來觀察隊列中operation狀態屬性的改變情況(isFinished, isExecuting, isCancelled)。當狀態發生了改變,隊列會受到一個KVO通知。為什么operationqueue要是用KVO呢?

消息的接收者(operation queue)明確的知道發送者(opertation),以及通過retain來控制operation的生命周期。另外,在這種情況下,只需要單向的消 息傳遞機制。當然,如果這樣考慮:如果operation queue只關心operation值的改變情況,可能還不足以說服大家使用KVO。但是我們至少可以這樣理解:什么機制可以對值的改變進行消息傳遞呢。

當然KVO也不是唯一的選擇。我們可以這樣設計:operation queue作為operation的delegate,operation會調用類似operationDidFinish: 或 operationDidBeginExecuting: 這樣的方法,來將它的state傳遞給queue。這樣一來,就不太方便了,因為operation需要將其state屬性保存下來,一遍調用這些 delegate方法。另外,由于queue不能主動獲取state信息,所以queue也必須保存著所有operation的state。

Notifications

Core Data使用notification來傳遞事件(例如一個managed object context內部的改變——NSManagedObjectContextDidChangeNotification)。

change notification是由managed object context發出的,所以我們不能確定消息的接收者一定知道發送者。如果消息并不是一個UI事件,而有可能多個接收者對該消息感興趣,并且消息的傳遞屬 于單向(one-way communication channel),那么notification是***選擇。

Delegation

Table view的delegate有多種功能,從accessory view的管理,到屏幕中cell顯示的跟蹤,都與delegate的功勞。例如,我們來看看 tableView:didSelectRowAtIndexPath: 方法。為什么要以delegate調用的方式來實現?而又為啥不用target-action方式?

正如我們在流程圖中看到的一樣,使用target-action時,不能傳遞自定義的數據。而在選中table view的某個cell時,collection view不僅僅需要告訴我們有一個cell被選中了,還需要告訴我們是哪個cell被選中了(index path)。按照這樣的一種思路,那么從流程圖中可以看到應該使用delegation機制。

如果消息傳遞中,不包含選中cell的index path,而是每當選中項改變時,我們主動去table view中獲取到選中cell的相關信息,會怎樣呢?其實這會非常的麻煩,因為這樣一來,我們就必須記住當前選中項相關數據,以便獲知被選中的cell。

同理,雖然我們也可以通過觀察table view中選中項的index paths屬性值,當該值發生改變時,獲得一個選中項改變的通知。不過,我們會遇到與上面同樣的問題:不做任何記錄的話,我們如何獲知被選中項的相關信息。

Blocks

關于block的介紹,我們來看看[NSURLSession dataTaskWithURL:completionHandler:]吧。從URL loading system返回到調用者,這個過程具體是如何傳遞消息的呢?首先,作為這個API的調用者,我們知道消息的發送者,但是我們并沒有retain這個發送 者。另外,這屬于單向消息傳遞——直接調用dataTaskWithURL:方法。如果按照這樣的思路對照著流程圖,我們會發現應該使用基于block消 息傳遞的機制。

還有其它可選的機制嗎?當然有了,蘋果自己的NSURLConnection就是***的例子。NSURLConnection在block問世之前 就已經存在了,所以它并沒有利用block進行消息傳遞,而是使用delegation機制。當block出現之后,蘋果在 NSURLConnection中添加了sendAsynchronousRequest:queue:completionHandler:方法 (OSX 10.7 iOS 5),因此如果是簡單的task,就不必在使用delegate了。

在OS X 10.9 和 iOS 7中,蘋果引入了一個非常modern的API:NSURLSession,其中使用block當做消息傳遞機制(NSURLSession仍然有一個delegate,不過是用于別的目的)。

Target-Action

Target-Action用的最明顯的一個地方就是button(按鈕)。button除了需要發送一個click事件以外,并不需要再發送別的信息了。所以Target-Action在用戶界面事件傳遞過程中,是***的選擇。

如果taget已經明確指定了,那么action消息回直接發送給指定的對象。如果taget是nil,action消息會以冒泡的方式在響應鏈中查找一個能夠處理該消息的對象。此時,我們擁有一種完全解耦的消息傳遞機制——發送者不需要知道接收者,以及其它一些信息。

Target-Action非常適用于用戶界面中的事件。目前也沒有其它合適的消息傳遞機制能夠提供同樣的功能。雖然notification最接 近這種在發送者和接收者解耦關系,但是target-action可以用于響應鏈(responder chain)——只有一個對象獲得action并作出響應,并且action可以在響應鏈中傳遞,直到遇到能夠響應該action的對象。

小結

***接觸這些機制,感覺它們都能用于兩個對象間的消息傳遞。但是仔細琢磨一番,會發現它們各自有其需求和功能。

文中給出的決策流程圖可以為我們選擇使用何種機制提供參考,不過圖中給出的方案并不是最終答案,好多地方還需要親自去實踐。

責任編輯:閆佳明 來源: beyondvincent
相關推薦

2017-12-21 15:42:08

iOS傳遞機制

2011-09-07 16:57:31

QT WidgetQWidget

2017-12-18 11:09:45

消息轉發DemoPerson

2009-01-03 11:07:06

AJAXASP.NET.NET

2009-01-03 16:29:45

AJAXASP.NET.NET

2013-04-11 12:40:16

Android消息機制

2010-08-02 16:59:54

2009-07-03 18:59:02

2023-09-08 08:01:40

Gateway測試配置

2017-02-20 09:46:59

2012-02-01 13:57:40

內存緩存機制

2011-08-18 10:59:57

iPhone開發消息通信NSNotificat

2023-03-27 15:39:53

微服務架構REST

2023-07-10 09:18:39

Redis訂閱模型

2024-06-04 15:56:48

Task?.NET異步編程

2011-05-31 11:55:00

Android 消息機制

2015-03-18 09:29:12

iOS開發爭議

2012-12-18 13:30:42

IBMdW

2009-08-27 10:01:27

ibmdw云計算

2011-06-08 10:06:32

C#
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91九色麻豆| 国产日韩精品一区 | 精品视频一区二区 | 亚洲a视频| 欧美天堂在线观看 | 久久一视频 | 午夜一级做a爰片久久毛片 精品综合 | 看毛片网站| 欧美男人天堂 | 免费久久久| 国产精品视屏 | 亚洲免费视频网站 | 欧美日韩一区二区在线观看 | 婷婷中文字幕 | 91精品国产91久久久久久吃药 | 日韩在线视频一区二区三区 | 欧美高清一区 | 国产激情视频在线观看 | 一二区视频 | 91精品国产91久久久久福利 | 亚洲欧洲成人 | 日韩一级二级片 | 午夜欧美a级理论片915影院 | 日本一区二区三区视频在线 | 狠狠干2020 | 国产日韩欧美 | 一区二区三区在线播放 | 国产精品久久 | 久久婷婷麻豆国产91天堂 | 日韩美女在线看免费观看 | 午夜专区 | 日日操操 | 欧美视频免费 | 日日夜夜视频 | 91精品国产91久久久久久吃药 | 99久久精品免费视频 | 99re视频 | 日韩精品久久久久久 | 91成人小视频 | 欧洲亚洲一区二区三区 | 人人玩人人添人人澡欧美 |