幾種消息傳遞機制的比較

本文主要借鑒了 objc上的文章

消息傳遞機制

iOS中,消息傳遞機制主要有 5種

  • KVO
  • Notification
  • Delegate
  • Target-Action
  • Block

本文會比較他們之間的區(qū)別和使用場景牛柒。

1. 介紹:

首先痊乾,我們了解一個概念: 消息的發(fā)送者和接受者哪审。

  • 對于delegate,tableView是發(fā)送者湿滓,它的delegate是接受者叽奥。
  • 對于Notification,postNotification的是發(fā)送者魔市, addObserve的是接受者赵哲。
  • 對于target-action, button是消息的發(fā)送者将宪,實現(xiàn)action的是消息的接受者。
  • 對于KVO印蔗,修改一個支持 KVO 的對象的對象是發(fā)送者燎潮,這個 KVO 對象的觀察者就是接收者。
  • 對于block,執(zhí)行該block的是發(fā)送者block() , 定義該block的block=^{ ... }的是接受者再菊。
1.1 KVO

KVO 是提供對象屬性被改變時的通知的機制纠拔,如果只對某個對象的值的改變感興趣的話,就可以使用 KVO 消息傳遞侦鹏。
有兩個前提:

  • 接收者(接收對象改變的通知的對象)需要知道發(fā)送者 (值會改變的對象)臀叙。 即接受者需要知道觀察的是誰的什么屬性。
  • 接收者需要知道發(fā)送者的生命周期渊涝,因為它需要在發(fā)送者被銷毀前注銷觀察者身份床嫌。
1.2 通知Notification

要在代碼中的兩個不相關(guān)的模塊中傳遞消息時厌处,通知機制是非常好的工具。通知機制廣播消息缆娃,當消息內(nèi)容豐富而且無需指望接收者一定要關(guān)注的話這一招特別有用洒敏。

  • 發(fā)送者和接收者不需要相互知道對方
  • 消息傳遞是單向的,我們不能回復(fù)一個通知
1.3 委托代理Delegate

delegate讓我們能自定義對象的行為郭毕,并發(fā)出一些觸發(fā)的事件的消息函荣。在相對接近的兩個模塊間傳遞消息扳肛。

  • 發(fā)送者需要知道接收者挖息,但是反過來沒有要求兽肤。因為發(fā)送者只需要知道接收者符合一定的協(xié)議,所以它們兩者結(jié)合的很松电禀。
  • 發(fā)送者可以用方法參數(shù)來傳遞消息內(nèi)容笤休,delegate 可以通過返回值的形式來給發(fā)送者作出回應(yīng)店雅。
1.4 Block

Block 通常可以完全替代 delegation 消息傳遞機制的角色沮明。

//問題:retain環(huán)
block會存在導(dǎo)致retain環(huán)的風(fēng)險亥揖。如果發(fā)送者需要 retain block 但又不能確保引用在什么時候被賦值為 nil, 那么所有在 block 內(nèi)對 self 的引用就會發(fā)生潛在的 retain 環(huán)摧扇。

self.myTableView.selectionHandler = ^void(NSIndexPatch* selectedIndexPath){
          //處理選擇
}

// 此處扛稽,self引用tableView滑负,tableView引用block。 tableView因為不知道什么時候不需要這個block了帮匾,所以不能把引用設(shè)置為nil虽惭。 如果block引用了self蛇尚,那么會形成引用環(huán)。

Operation使用block的處理方式

self.queue = [[NSOperationQueue alloc] init];
MyOperation *operation = [[MyOperation alloc] init];
operation.completionBlock = ^{
    [self finishedOperation];
};
[self.queue addOperation:operation];

// self retain了queue匆笤,queue retain了Operation谱邪, Operation 又retain了completionBlock虾标,而completionBlock 又retain了self灌砖。 把 operation 加入 queue 中會使 operation 在某個時間被執(zhí)行,然后被從 queue 中移除蘸吓。(如果沒被執(zhí)行撩幽,問題就大了窜醉。)一旦 queue 把 operation 移除,retain 環(huán)就被打破了拜英。

另外一個例子

@interface Encoder ()
@property (nonatomic, copy) void (^completionHandler)();
@end

@implementation Encoder

- (void)encodeWithCompletionHandler:(void (^)())handler
{
    self.completionHandler = handler;
    // 進行異步處理...
}

// 這個方法會在完成后被調(diào)用一次
- (void)finishedEncoding
{
    self.completionHandler();
    self.completionHandler = nil; // <- 不要忘了這個!
}
@end

// 寫一個視頻編碼類琅催, 一旦任務(wù)完成了藤抡,completion blcok被調(diào)用后,我們把它設(shè)定為nil弄兜。

如上邊的例子,retain環(huán)的解決方式就是打破引用環(huán)挨队。

block和delegate相比:

    1. 一個被調(diào)用的方法需要發(fā)送一個一次性的消息作為回復(fù),那么使用 block 是很好的選擇湿弦,直接使用nil打破retain環(huán)腾夯。
    1. 使用 weak和strong 關(guān)鍵字來打破引用環(huán)(我們通常的做法)。
    1. 如果將處理的消息和對消息的調(diào)用放在一起可以增強可讀性的話班利,我們也很難拒絕使用 block 來進行處理罗标。
1.5 target-action

Target-Action 是回應(yīng) UI 事件時典型的消息傳遞方式积蜻。

  • 消息的接收者不知道發(fā)送者。
  • 如果接受者是 nil宙拉,action 會在響應(yīng)鏈中被傳遞下去丙笋,直到找到一個響應(yīng)它的對象
  • 發(fā)送的消息不能攜帶自定義的信息

2. 正確的選擇

communication-patterns-flow-chart.png
  • 有個方框中說:發(fā)送者支持KVO御板, 不僅僅是說發(fā)送者會在值改變的時候發(fā)送 KVO 通知稳吮,而且說明觀察者需要知道發(fā)送者的生命周期。如果發(fā)送者被存在一個 weak 屬性中列林,那么發(fā)送者有可能會自己變成 nil酪惭,那時觀察者會導(dǎo)致內(nèi)存泄露。

  • 一個在最后一行的框里說砌创,消息直接響應(yīng)方法調(diào)用。也就是說方法調(diào)用的接收者需要給調(diào)用者一個消息作為方法調(diào)用的直接反饋刽辙。這也就是說處理消息的代碼和調(diào)用方法的代碼必須在同一個地方甲献。

  • 在右下角的地方,一個選擇分支這樣說:發(fā)送者能確保釋放對 block 的引用嗎慨灭? 就是retain環(huán)那個球及。

3. framework示例
3.1 KVO

NSOperationQueue 用了 KVO 觀察隊列中的 operation 狀態(tài)屬性的改變情況 (isFinished,isExecuting筹陵,isCancelled)镊尺。當狀態(tài)改變的時候鹅心,隊列會收到 KVO 通知.

消息的接收者(operation 隊列)知道消息的發(fā)送者(operation)纺荧,并 retain 它并控制后者的生命周期。另外输枯,在這種情況下只需要單向的消息傳遞機制占贫。


kvo-flow-chart.png
3.2 Notification

Core Data 使用 notification 傳遞事件.

接收者不知道消息的發(fā)送者型奥。因為消息的源頭不是一個 UI 事件厢汹,很多接收者可能在關(guān)注著此消息,并且消息傳遞是單向的界弧,所以 notification 是唯一可行的選擇。


notification-flow-chart.png
3.3 Delegate

Table view 的 delegate 有多重功能划栓。

用 target-action 時忠荞,不能傳遞自定義的數(shù)據(jù)月匣。而選中 table view 的某個 cell 時,collection view 不僅需要告訴我們一個 cell 被選中了素标,也要通過 index path 告訴我們哪個 cell 被選中了萍悴。如果我們照著這個思路癣诱,流程圖會引導(dǎo)我們使用 delegation 機制。
delegation-flow-chart.png
3.4 block

我們用 -[NSURLSession dataTaskWithURL:completionHandler:] 來作為一個 block API 的介紹鲫惶。

作為 API 的調(diào)用者欠母,我們知道消息的發(fā)送者吆寨,但是我們并沒有 retain 它。另外六水,這是個單向的消息傳遞————它直接調(diào)用 dataTaskWithURL: 的方法掷贾。如果我們對照流程圖荣茫,會發(fā)現(xiàn)這屬于 block 消息傳遞機制。
block-flow-chart.png
3.5 target-action

一個明顯的 target-action 用例是按鈕憎乙。按鈕在不被按下的時候不需要發(fā)送任何的信息叉趣。為了這個目的,target-action 是 UI 中消息傳遞的最佳選擇阵谚。


target-action-flow-chart.png

如果 target 是明確指定的梢什,那么 action 消息會發(fā)送給指定的對象朝聋。如果 target 是 nil冀痕, action 消息會一直在響應(yīng)鏈中被傳遞下去,直到找到一個能處理它的對象僻他。在這種情況下腊尚,我們有一個完全解耦的消息傳遞機制:發(fā)送者不需要知道接收者婿斥,反之亦然。Target-action 機制非常適合響應(yīng) UI 的事件

4. 總結(jié)

我個人的總結(jié):

  • 發(fā)送者不知道接受者,接受者不知道發(fā)送者: target-action(沒有自定義消息) Notification(自定義消息)勘高。 (target-action有響應(yīng)者鏈)可以有多個接受者坟桅,單向消息傳遞。

  • 接受者知道發(fā)送者赖舟,:KVO(主要是屬性值的改變消息)夸楣,可以有多個接受者,單向消息傳遞石洗。

  • 發(fā)送者知道接受者: block(可能有retain環(huán)讲衫,消息的發(fā)送和接受在一個地方),delegate招驴, 雙向消息傳遞枷畏,靈活的自定義消息。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丹允,一起剝皮案震驚了整個濱河市袋倔,隨后出現(xiàn)的幾起案子宾娜,更是在濱河造成了極大的恐慌,老刑警劉巖嚣艇,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件食零,死亡現(xiàn)場離奇詭異寂屏,居然都是意外死亡迁霎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門秘豹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昌粤,“玉大人既绕,你說我怎么就攤上這事啄刹。” “怎么了岸更?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵鸵膏,是天一觀的道長。 經(jīng)常有香客問我怎炊,道長谭企,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任评肆,我火速辦了婚禮债查,結(jié)果婚禮上瓜挽,老公的妹妹穿的比我還像新娘盹廷。我一直安慰自己,他們只是感情好久橙,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布俄占。 她就那樣靜靜地躺著,像睡著了一般淆衷。 火紅的嫁衣襯著肌膚如雪缸榄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天祝拯,我揣著相機與錄音甚带,去河邊找鬼。 笑死佳头,一個胖子當著我的面吹牛鹰贵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播康嘉,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碉输,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亭珍?” 一聲冷哼從身側(cè)響起敷钾,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎块蚌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膘格,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡峭范,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瘪贱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纱控。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辆毡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甜害,到底是詐尸還是另有隱情舶掖,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布尔店,位于F島的核電站眨攘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嚣州。R本人自食惡果不足惜鲫售,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望该肴。 院中可真熱鬧情竹,春花似錦、人聲如沸匀哄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涎嚼。三九已至阱州,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铸抑,已是汗流浹背贡耽。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鹊汛,地道東北人蒲赂。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像刁憋,于是被迫代替她去往敵國和親滥嘴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 每個應(yīng)用或多或少都由一些需要相互傳遞消息的對象結(jié)合起來以完成任務(wù)至耻。在這篇文章里若皱,我們將介紹所有可用的消息傳遞機制,...
    愛敲代碼的果果閱讀 3,969評論 0 2
  • 每個應(yīng)用程序或多或少尘颓,都由一些松耦合的對象構(gòu)成走触,這些對象彼此之間要想很好的完成任務(wù),就需要進行消息傳遞疤苹。 本文將介...
    石丘閱讀 1,720評論 4 7
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,386評論 8 265
  • 外出吃晚飯互广,遇見同事吃宵夜,見識了她健談卻非常有原則的兒子:問他喝不喝飲料,他說惫皱,我飲料就不喝了像樊,對身體不好,我只...
    老瑜頭閱讀 296評論 0 0
  • 這樣溫柔的霧靄旅敷,幾時能有啊生棍。 晨光熹微,車笛輕鳴媳谁,打開窗戶就能聞到隔夜發(fā)了酵的泥土之芬芳涂滴。飛鳥在高處撲棱著翅膀,天...
    浮辭z閱讀 124評論 0 0