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

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

文章分類:公司動(dòng)態(tài) 發(fā)布時(shí)間:2014-03-25 原文作者:tbkj 閱讀( )

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

  消息的接收者(operation queue)明確的知道發(fā)送者(opertation),以及通過retain來控制operation的生命周期。另外,在這種情況下,只需要單向的消 息傳遞機(jī)制。當(dāng)然,如果這樣考慮:如果operation queue只關(guān)心operation值的改變情況,可能還不足以說服大家使用KVO。但是我們至少可以這樣理解:什么機(jī)制可以對(duì)值的改變進(jìn)行消息傳遞呢。
  當(dāng)然KVO也不是唯一的選擇。我們可以這樣設(shè)計(jì):operation queue作為operation的delegate,operation會(huì)調(diào)用類似operationDidFinish: 或 operationDidBeginExecuting: 這樣的方法,來將它的state傳遞給queue。這樣一來,就不太方便了,因?yàn)閛peration需要將其state屬性保存下來,一遍調(diào)用這些 delegate方法。另外,由于queue不能主動(dòng)獲取state信息,所以queue也必須保存著所有operation的state。
  Notifications
  Core Data使用notification來傳遞事件(例如一個(gè)managed object context內(nèi)部的改變——NSManagedObjectContextDidChangeNotification)。
  change notification是由managed object context發(fā)出的,所以我們不能確定消息的接收者一定知道發(fā)送者。如果消息并不是一個(gè)UI事件,而有可能多個(gè)接收者對(duì)該消息感興趣,并且消息的傳遞屬 于單向(one-way communication channel),那么notification是最佳選擇。
  Delegation
  Table view的delegate有多種功能,從accessory view的管理,到屏幕中cell顯示的跟蹤,都與delegate的功勞。例如,我們來看看 tableView:didSelectRowAtIndexPath: 方法。為什么要以delegate調(diào)用的方式來實(shí)現(xiàn)?而又為啥不用target-action方式?
  正如我們?cè)诹鞒虉D中看到的一樣,使用target-action時(shí),不能傳遞自定義的數(shù)據(jù)。而在選中table view的某個(gè)cell時(shí),collection view不僅僅需要告訴我們有一個(gè)cell被選中了,還需要告訴我們是哪個(gè)cell被選中了(index path)。按照這樣的一種思路,那么從流程圖中可以看到應(yīng)該使用delegation機(jī)制。
  如果消息傳遞中,不包含選中cell的index path,而是每當(dāng)選中項(xiàng)改變時(shí),我們主動(dòng)去table view中獲取到選中cell的相關(guān)信息,會(huì)怎樣呢?其實(shí)這會(huì)非常的麻煩,因?yàn)檫@樣一來,我們就必須記住當(dāng)前選中項(xiàng)相關(guān)數(shù)據(jù),以便獲知被選中的cell。
  同理,雖然我們也可以通過觀察table view中選中項(xiàng)的index paths屬性值,當(dāng)該值發(fā)生改變時(shí),獲得一個(gè)選中項(xiàng)改變的通知。不過,我們會(huì)遇到與上面同樣的問題:不做任何記錄的話,我們?nèi)绾潍@知被選中項(xiàng)的相關(guān)信息。
  Blocks
  關(guān)于block的介紹,我們來看看[NSURLSession dataTaskWithURL:completionHandler:]吧。從URL loading system返回到調(diào)用者,這個(gè)過程具體是如何傳遞消息的呢?首先,作為這個(gè)API的調(diào)用者,我們知道消息的發(fā)送者,但是我們并沒有retain這個(gè)發(fā)送 者。另外,這屬于單向消息傳遞——直接調(diào)用dataTaskWithURL:方法。如果按照這樣的思路對(duì)照著流程圖,我們會(huì)發(fā)現(xiàn)應(yīng)該使用基于block消 息傳遞的機(jī)制。
  還有其它可選的機(jī)制嗎?當(dāng)然有了,蘋果自己的NSURLConnection就是最好的例子。NSURLConnection在block問世之前 就已經(jīng)存在了,所以它并沒有利用block進(jìn)行消息傳遞,而是使用delegation機(jī)制。當(dāng)block出現(xiàn)之后,蘋果在 NSURLConnection中添加了sendAsynchronousRequest:queue:completionHandler:方法 (OSX 10.7 iOS 5),因此如果是簡(jiǎn)單的task,就不必在使用delegate了。
  在OS X 10.9 和 iOS 7中,蘋果引入了一個(gè)非常modern的API:NSURLSession,其中使用block當(dāng)做消息傳遞機(jī)制(NSURLSession仍然有一個(gè)delegate,不過是用于別的目的)。
  Target-Action
  Target-Action用的最明顯的一個(gè)地方就是button(按鈕)。button除了需要發(fā)送一個(gè)click事件以外,并不需要再發(fā)送別的信息了。所以Target-Action在用戶界面事件傳遞過程中,是最佳的選擇。
  如果taget已經(jīng)明確指定了,那么action消息回直接發(fā)送給指定的對(duì)象。如果taget是nil,action消息會(huì)以冒泡的方式在響應(yīng)鏈中查找一個(gè)能夠處理該消息的對(duì)象。此時(shí),我們擁有一種完全解耦的消息傳遞機(jī)制——發(fā)送者不需要知道接收者,以及其它一些信息。
  Target-Action非常適用于用戶界面中的事件。目前也沒有其它合適的消息傳遞機(jī)制能夠提供同樣的功能。雖然notification最接 近這種在發(fā)送者和接收者解耦關(guān)系,但是target-action可以用于響應(yīng)鏈(responder chain)——只有一個(gè)對(duì)象獲得action并作出響應(yīng),并且action可以在響應(yīng)鏈中傳遞,直到遇到能夠響應(yīng)該action的對(duì)象。
  
原文來自:tbkj
?