黄色毛片在线观看,亚洲人成网站77777·c0m,美女免费黄色av网站在线观看,五月丁香综合网站婷婷,噜噜噜噜私人影院av线观看,天天色av,在线观看黄片欧洲

探索iOS開發(fā)中的消息傳遞機制

文章分類:公司動態(tài) 發(fā)布時間:2014-04-04 原文作者:tbkj 閱讀( )

每個應用程序或多或少,都由一些松耦合的對象構成,這些對象彼此之間要想很好的完成任務,就需要進行消息傳遞。本文將介紹所有可用的消息傳遞機制,并通過 示例來介紹這些機制在蘋果的Framework中如何使用,同時,還介紹了一些最佳實踐建議,告訴你什么時機該選擇使用什么機制。
  雖然這一期的主題是關于Foundation Framework的,不過本文中還介紹了一些超出Foundation Framework(KVO和Notification)范圍的一些消息傳遞機制,另外還介紹了delegation,block和target- action。
  大多數情況下,消息傳遞該使用什么機制,是很明確的了,當然了,在某些情況下該使用什么機制并沒有明確的答案,需要你親自去嘗試一下。
  本文中,會經常提及接收者[recipient]和發(fā)送者[sender]。在消息傳遞 機制中具體是什么意思,我們可以通過一個示例來解釋:一個table view是發(fā)送者,而它的delegate就是接收者。Core Data managed object context是notification的發(fā)送者,而獲取這些notification的主體則是接收者。一個滑塊(slider)是action消息 的發(fā)送者,而在代碼里面對應著實現這個action的responder就是接收者。對象中的某個屬性支持KVO,那么誰修改這個值,誰就是發(fā)送者,對應 的觀察者(observer)則是接收者。
  可用的機制
  首先我們來看看每種機制的具體特點。在下一節(jié)中,我會結合一個流程圖來介紹如何在具體情況下,選擇正確的消息傳遞機制。最后,將介紹一些來自蘋果Framework中的示例,并會解釋在某種確定情況下為什么要選擇固定的機制。
  KVO
  KVO提供了這樣一種機制:當對象中的某個屬性值發(fā)生了改變,可以對這些值的觀察者做出通知。KVO的實現包含在Foundation里面,基于 Foundation構建的許多Framework對KVO都有所依賴。要想了解更多關于如何使用KVO,可以閱讀本期由Daniel寫的的KVO和KVC文章。
  如果對某個對象中值的改變情況感興趣,那么可以使用KVO消息傳遞機制。這里有兩個要求,首先,接收者(會接收到值發(fā)生改變的消息)必須知道發(fā)送者 (值將發(fā)生改變的那個對象)。另外,接收者同樣還需要知道發(fā)送者的生命周期,因為在銷毀發(fā)送者對象之前,需要取消觀察者的注冊。如果這兩個要求都滿足了, 消息傳遞過程中可以是1對多(多個觀察者可以注冊某個對象中的值)。
  如果計劃在Core Data對象上使用KVO,需要知道這跟一般的KVO使用方法有點不同。那就是必須結合Core Data的故障機制(faulting mechanism),一旦core data出現了故障,它將會觸發(fā)其屬性對應的觀察者(即使這些屬性值沒有發(fā)生改變)。
  Notification
  在不相關的兩部分代碼中要想進行消息傳遞,通知(notifacation)是非常好的一種機制,它可以對消息進行廣播。特別是想要傳遞豐富的信息,并且不一定指望有誰對此消息關心。
  通知可以用來發(fā)送任意的消息,甚至包含一個userInfo字典,或者是NSNotifacation的一個子類。通知的獨特之處就在于發(fā)送者和接 收者雙方并不需要相互知道。這樣就可以在非常松耦合的模塊間進行消息的傳遞。記住,這種消息傳遞機制是單向的,作為接收者是不可以回復消息的。
  delegation
  在蘋果的Framework中,delegation模式被廣泛的只用著。delegation允許我們定制某個對象的行為,并且可以收到某些確定 的事件。為了使用delegation模式,消息的發(fā)送者需要知道消息的接收者(delegate),反過來就不用了。這里的發(fā)送者和接收者是比較松耦合 的,因為發(fā)送者只知道它的delegate是遵循某個特定的協(xié)議。
  delegate協(xié)議可以定義任意的方法,因此你可以準確的定義出你所需要的類型。你可以用函數參數的形式來處理消息內容,delegate還可以 通過返回值的形式給發(fā)送者做出回應。如果只需要在相對接近的兩個模塊之間進行消息傳遞,那么Delegation是一種非常靈活和直接方式。
  不過,過渡使用delegation也有一定的風險,如果兩個對象的耦合程度比較緊密,相互之間不能獨立存在,那么此時就沒有必要使用 delegate協(xié)議了,針對這種情況,對象之間可以知道相互間的類型,進而直接進行消息傳遞。例如UICollectionViewLayout和 NSURLSessionConfiguration。
  block
  Block相對來說,是一種比較新的技術,它首次出現是在OS X 10.6和iOS 4中。一般情況下,block可以滿足用delegation實現的消息傳遞機制。不過這兩種機制都有各自的需求和優(yōu)勢。
  當不考慮使用block時,一般主要是考慮到block極易引起retain環(huán)。如果發(fā)送者需要reatain block,而又不能確保這個引用什么時候被nil,這樣就會發(fā)生潛在的retain環(huán)。
  假設我們想要實現一個table view,使用block替代delegate,來當做selection的回調,如下:
  self.myTableView.selectionHandler = ^void(NSIndexPath *selectedIndexPath) { // handle selection ... };
  上面代碼的問題在于self retain了table view,而table view為了之后能夠使用block,進而 retain了block。而table view又不能把這個引用nil掉,因為它不知道什么時候不在需要這個block了。如果我們保證不了可以打破這個retain環(huán),而我們又需要 retain發(fā)送者,此時block不是好的選擇。
  NSOperation就可以很好的使用block,因為它能再某個時機打破retain環(huán):
  self.queue = [[NSOperationQueue alloc] init]; MyOperation *operation = [[MyOperation alloc] init]; operation.completionBlock = ^{ [self finishedOperation]; }; [self.queue addOperation:operation];
  乍一看這似乎是一個retain環(huán):self retain了queue,queue retain了operation,而operation retain了completion block,而completion blockretain了self。不過,在這里,將operation添加到queue時,會使operation在某個時機被執(zhí)行,然后從queue 中remove掉(如果沒有被執(zhí)行,就會有大問題了)。一單queue移除了operation之后,retain環(huán)就被打破了。
  再來一個示例:這里實現了一個視頻編碼器的類,里面有一個名為encodeWithCompletionHandler:的方法。為了避免出現retain環(huán),我們需要確保編碼器這個對象能夠在某個時機nil掉其對block的引用。其內部代碼如下所示:
  @interface Encoder () @property (nonatomic, copy) void (^completionHandler)(); @end @implementation Encoder - (void)encodeWithCompletionHandler:(void (^)())handler { self.completionHandler = handler; // do the asynchronous processing... } // This one will be called once the job is done - (void)finishedEncoding { self.completionHandler(); self.completionHandler = nil; // <- Don't forget this! } @end
  在上面的代碼中,一旦編碼任務完成,就會調用complietion block,進而把引用nil掉。
  如果我們發(fā)送的消息屬于一次性的(具體到某個方法的調用),由于這樣可以打破潛在的retain環(huán),那么使用block是非常不錯的選擇。另外,如 果為了讓代碼可讀性更強,更有連貫性,那最好是使用block了。根據這個思路,block經??梢杂糜赾ompletion handler、error handler等。
  Target-Action
  Target-Action主要被用于響應用戶界面事件時所需要傳遞的消息中。iOS中的UIControl和Mac中的 NSControl/NSCell都支持這種機制。Target-Action在消息的發(fā)送者和接收者之間建立了一個非常松散耦合。消息的接收者不知道發(fā) 送者,甚至消息的發(fā)送者不需要預先知道消息的接收者。如果target是nil,action會在響應鏈(responder chain)中被傳遞,知道找到某個能夠響應該aciton的對象。在iOS中,每個控件都能關聯多個target-action。
  基于target-action消息傳遞的機制有一個局限就是發(fā)送的消息不能攜帶自定義的payload。在Mac的action方法中,接收者總 是被放在第一個參數中。而在iOS中,可以選擇性的將發(fā)送者和和觸發(fā)action的事件作為參數。除此之外,沒有別的辦法可以對發(fā)送action消息內容 做控制。
  做出正確的選擇
  根據上面討論的結果,這里我畫了一個流程圖,來幫助我們何時使用什么消息傳遞機制做出更好的決定。忠告:流程圖中的建議并非最終的答案;可能還有別的選項依然能實現目的。只不過大多數情況下此圖可以引導你做出正確的決定。
  上圖中,還有一些細節(jié)需要做更近一步的解釋:
  上圖中的有個盒子這樣說到:sender is KVO compliant(發(fā)送者支持compliant)。這不僅以意味著當值發(fā)生改變時,發(fā)送者會發(fā)送KVO通知,并且觀察者還需要知道發(fā)送者的生命周期。 如果發(fā)送者被存儲在一個weak屬性中,那么發(fā)送者有可能被nil掉,進而引起觀察者發(fā)生leak。
  另外底部的一個盒子說到:message is direct response to method call(消息直接在方法的調用代碼中響應)。也就是說處理消息的代碼跟方法的調用代碼處于相同的地方。
  最后,在左下角,處于一個決策問題的判斷狀態(tài):sender can guarantee to nil out reference to block?(發(fā)送者能夠確保nil掉到block的引用嗎?),這實際上涉及到之前我們討論到基于block 的APIs已經潛在的retain環(huán)。使用block時,如果發(fā)送者不能保證在某個實際能夠把對block的引用nil掉,那么將會遇到retain環(huán)的 問題。
  Framework示例
  本節(jié)我們通過一些來自蘋果Framework的示例,來看看在實際使用某種機制之前,蘋果是處于何種原因做出選擇的。
  KVO
  NSOperationQueue就是lion給了KVO來觀察隊列中operation狀態(tài)屬性的改變情況(isFinished, isExecuting, isCancelled)。當狀態(tài)發(fā)生了改變,隊列會受到一個KVO通知。為什么operationqueue要是用KVO呢?
  消息的接收者(operation queue)明確的知道發(fā)送者(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發(fā)出的,所以我們不能確定消息的接收者一定知道發(fā)送者。如果消息并不是一個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屬性值,當該值發(fā)生改變時,獲得一個選中項改變的通知。不過,我們會遇到與上面同樣的問題:不做任何記錄的話,我們如何獲知被選中項的相關信息。
  Blocks
  關于block的介紹,我們來看看[NSURLSession dataTaskWithURL:completionHandler:]吧。從URL loading system返回到調用者,這個過程具體是如何傳遞消息的呢?首先,作為這個API的調用者,我們知道消息的發(fā)送者,但是我們并沒有retain這個發(fā)送 者。另外,這屬于單向消息傳遞——直接調用dataTaskWithURL:方法。如果按照這樣的思路對照著流程圖,我們會發(fā)現應該使用基于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除了需要發(fā)送一個click事件以外,并不需要再發(fā)送別的信息了。所以Target-Action在用戶界面事件傳遞過程中,是最佳的選擇。
  如果taget已經明確指定了,那么action消息回直接發(fā)送給指定的對象。如果taget是nil,action消息會以冒泡的方式在響應鏈中查找一個能夠處理該消息的對象。此時,我們擁有一種完全解耦的消息傳遞機制——發(fā)送者不需要知道接收者,以及其它一些信息。
  Target-Action非常適用于用戶界面中的事件。目前也沒有其它合適的消息傳遞機制能夠提供同樣的功能。雖然notification最接 近這種在發(fā)送者和接收者解耦關系,但是target-action可以用于響應鏈(responder chain)——只有一個對象獲得action并作出響應,并且action可以在響應鏈中傳遞,直到遇到能夠響應該action的對象。
  小結
  首次接觸這些機制,感覺它們都能用于兩個對象間的消息傳遞。但是仔細琢磨一番,會發(fā)現它們各自有其需求和功能。
  文中給出的決策流程圖可以為我們選擇使用何種機制提供參考,不過圖中給出的方案并不是最終答案,好多地方還需要親自去實踐。
原文來自:tbkj
?