本文主要借鑒了 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相比:
- 一個被調(diào)用的方法需要發(fā)送一個一次性的消息作為回復(fù),那么使用 block 是很好的選擇湿弦,直接使用nil打破retain環(huán)腾夯。
- 使用 weak和strong 關(guān)鍵字來打破引用環(huán)(我們通常的做法)。
- 如果將處理的消息和對消息的調(diào)用放在一起可以增強可讀性的話班利,我們也很難拒絕使用 block 來進行處理罗标。
1.5 target-action
Target-Action 是回應(yīng) UI 事件時典型的消息傳遞方式积蜻。
- 消息的接收者不知道發(fā)送者。
- 如果接受者是
nil
宙拉,action 會在響應(yīng)鏈中被傳遞下去丙笋,直到找到一個響應(yīng)它的對象 - 發(fā)送的消息不能攜帶自定義的信息
2. 正確的選擇
有個方框中說:發(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 它并控制后者的生命周期。另外输枯,在這種情況下只需要單向的消息傳遞機制占贫。
3.2 Notification
Core Data 使用 notification 傳遞事件.
接收者不知道消息的發(fā)送者型奥。因為消息的源頭不是一個 UI 事件厢汹,很多接收者可能在關(guān)注著此消息,并且消息傳遞是單向的界弧,所以 notification 是唯一可行的選擇。
3.3 Delegate
Table view 的 delegate 有多重功能划栓。
用 target-action 時忠荞,不能傳遞自定義的數(shù)據(jù)月匣。而選中 table view 的某個 cell 時,collection view 不僅需要告訴我們一個 cell 被選中了素标,也要通過 index path 告訴我們哪個 cell 被選中了萍悴。如果我們照著這個思路癣诱,流程圖會引導(dǎo)我們使用 delegation 機制。3.4 block
我們用 -[NSURLSession dataTaskWithURL:completionHandler:] 來作為一個 block API 的介紹鲫惶。
3.5 target-action
一個明顯的 target-action 用例是按鈕憎乙。按鈕在不被按下的時候不需要發(fā)送任何的信息叉趣。為了這個目的,target-action 是 UI 中消息傳遞的最佳選擇阵谚。
如果 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招驴, 雙向消息傳遞枷畏,靈活的自定義消息。