在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 消息傳遞機(jī)制
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

消息傳遞機(jī)制

每個(gè)應(yīng)用或多或少都由一些需要相互傳遞消息的對(duì)象結(jié)合起來以完成任務(wù)。在這篇文章里,我們將介紹所有可用的消息傳遞機(jī)制,并通過例子來介紹怎樣在蘋果的框架里使用。我們還會(huì)選擇一些最佳范例來介紹什么時(shí)候該用什么機(jī)制。

雖然這一期的主題是關(guān)于 Foundation 框架的,但是我們會(huì)超出 Foundation 的消息傳遞機(jī)制 (KVO 和 通知) 來講一講 delegation,block 和 target-action 幾種機(jī)制。

當(dāng)然,有些情況下該使用什么機(jī)制沒有唯一的答案,所以應(yīng)該按照自己的喜好去試試。另外大多數(shù)情況下該使用什么機(jī)制應(yīng)該是很清楚的。

本文中,我們會(huì)常常提及“接收者”和“發(fā)送者”。它們?cè)谙鬟f中的意思可以通過以下的例子解釋:一個(gè) table view 是發(fā)送者,它的 delegate 就是接收者。Core Data managed object context 是它所發(fā)出的 notification 的發(fā)送者,獲取 notification 的就是接收者。一個(gè)滑塊 (slider) 是 action 消息的發(fā)送者,而實(shí)現(xiàn)這個(gè) action (方法)的是它的接收者。任何修改一個(gè)支持 KVO 的對(duì)象的對(duì)象是發(fā)送者,這個(gè) KVO 對(duì)象的觀察者就是接收者。明白精髓了嗎?

幾種消息傳遞機(jī)制

首先我們來看看每種機(jī)制的具體特點(diǎn)。在這個(gè)基礎(chǔ)上,下一節(jié)我們會(huì)畫一個(gè)流程圖來幫我們?cè)诰唧w情況下正確選擇應(yīng)該使用的機(jī)制。最后,我們會(huì)介紹一些蘋果框架里的例子并且解釋為什么在那些用例中會(huì)選擇這樣的機(jī)制。

KVO

KVO 是提供對(duì)象屬性被改變時(shí)的通知的機(jī)制。KVO 的實(shí)現(xiàn)在 Foundation 中,很多基于 Foundation 的框架都依賴它。想要了解更多有關(guān) KVO 的最佳實(shí)踐,請(qǐng)閱讀本期 Daniel 寫的 KVO 和 KVC 文章。

如果只對(duì)某個(gè)對(duì)象的值的改變感興趣的話,就可以使用 KVO 消息傳遞。不過有一些前提:第一,接收者(接收對(duì)象改變的通知的對(duì)象)需要知道發(fā)送者 (值會(huì)改變的對(duì)象);第二,接收者需要知道發(fā)送者的生命周期,因?yàn)樗枰诎l(fā)送者被銷毀前注銷觀察者身份。如果這兩個(gè)要去符合的話,這個(gè)消息傳遞機(jī)制可以一對(duì)多(多個(gè)觀察者可以注冊(cè)觀察同一個(gè)對(duì)象的變化)

如果要在 Core Data 上使用 KVO 的話,方法會(huì)有些許差別。這和 Core Data 的惰性加載 (faulting) 機(jī)制有關(guān)。一旦一個(gè) managed object 被惰性加載處理的話,即使它的屬性沒有被改變,它還是會(huì)觸發(fā)相應(yīng)的觀察者。

編者注 把屬性值先取入緩存中,在對(duì)象需要的時(shí)候再進(jìn)行一次訪問,這在 Core Data 中是默認(rèn)行為,這種技術(shù)稱為 Faulting。這么做可以避免降低內(nèi)存開銷,但是如果你確定將訪問結(jié)果對(duì)象的具體屬性值時(shí),可以禁用 Faults 以提高獲取性能。關(guān)于這個(gè)技術(shù)更多的情況,請(qǐng)移步[官方文檔](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdFaultingUniquing.html)

通知

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

通知可以用來發(fā)送任意消息,甚至可以包含一個(gè) userInfo 字典。你也可以繼承 NSNotification 寫一個(gè)自己的通知類來自定義行為。通知的獨(dú)特之處在于,發(fā)送者和接收者不需要相互知道對(duì)方,所以通知可以被用來在不同的相隔很遠(yuǎn)的模塊之間傳遞消息。這就意味著這種消息傳遞是單向的,我們不能回復(fù)一個(gè)通知。

委托 (Delegation)

Delegation 在蘋果的框架中廣泛存在。它讓我們能自定義對(duì)象的行為,并收到一些觸發(fā)的事件。要使用 delegation 模式的話,發(fā)送者需要知道接收者,但是反過來沒有要求。因?yàn)榘l(fā)送者只需要知道接收者符合一定的協(xié)議,所以它們兩者結(jié)合的很松。

因?yàn)?delegate 協(xié)議可以定義任何的方法,我們可以照著自己的需求來傳遞消息??梢杂梅椒▍?shù)來傳遞消息內(nèi)容,delegate 可以通過返回值的形式來給發(fā)送者作出回應(yīng)。如果只要在相對(duì)接近的兩個(gè)模塊間傳遞消息,delgation 是很靈活很直接的消息傳遞機(jī)制。

過度使用 delegation 也會(huì)帶來風(fēng)險(xiǎn)。如果兩個(gè)對(duì)象結(jié)合得很緊密,任何其中一個(gè)對(duì)象都不能單獨(dú)運(yùn)轉(zhuǎn),那么就不需要用 delegate 協(xié)議了。這些情況下,對(duì)象已經(jīng)知道各自的類型,可以直接交流。兩個(gè)比較新的例子是 UICollectionViewLayoutNSURLSessionConfiguration。

Block

Block 是最近才加入 Objective-C 的,首次出現(xiàn)在 OS X 10.6 和 iOS 4 平臺(tái)上。Block 通??梢酝耆娲?delegation 消息傳遞機(jī)制的角色。不過這兩種機(jī)制都有它們自己的獨(dú)特需求和優(yōu)勢(shì)。

一個(gè)不使用 block 的理由通常是 block 會(huì)存在導(dǎo)致 retain 環(huán) (retain cycles) 的風(fēng)險(xiǎn)。如果發(fā)送者需要 retain block 但又不能確保引用在什么時(shí)候被賦值為 nil, 那么所有在 block 內(nèi)對(duì) self 的引用就會(huì)發(fā)生潛在的 retain 環(huán)。

假設(shè)我們要實(shí)現(xiàn)一個(gè)用 block 回調(diào)而不是 delegate 機(jī)制的 table view 里的選擇方法,如下所示:

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

這兒的問題是,self 會(huì) retain table view,table view 為了讓 block 之后可以使用而又需要 retain 這個(gè) block。然而 table view 不能把這個(gè)引用設(shè)為 nil,因?yàn)樗恢朗裁磿r(shí)候不需要這個(gè) block 了。如果我們不能保證打破 retain 環(huán)并且我們需要 retain 發(fā)送者,那么 block 就不是一個(gè)的好選擇。

NSOperation 是使用 block 的一個(gè)好范例。因?yàn)樗谝欢ǖ牡胤酱蚱屏?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 了 completionBlock, 而 completionBlock retain 了 self。然而,把 operation 加入 queue 中會(huì)使 operation 在某個(gè)時(shí)間被執(zhí)行,然后被從 queue 中移除。(如果沒被執(zhí)行,問題就大了。)一旦 queue 把 operation 移除,retain 環(huán)就被打破了。

另一個(gè)例子是:我們?cè)趯懸粋€(gè)視頻編碼器的類,在類里面我們會(huì)調(diào)用一個(gè) encodeWithCompletionHandler: 的方法。為了不出問題,我們需要保證編碼器對(duì)象在某個(gè)時(shí)間點(diǎn)會(huì)釋放對(duì) block 的引用。其代碼如下所示:

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

@implementation Encoder

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

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

@end

一旦任務(wù)完成,completion block 調(diào)用過了以后,我們就應(yīng)該把它設(shè)為 nil。

如果一個(gè)被調(diào)用的方法需要發(fā)送一個(gè)一次性的消息作為回復(fù),那么使用 block 是很好的選擇, 因?yàn)檫@樣做我們可以打破潛在的 retain 環(huán)。另外,如果將處理的消息和對(duì)消息的調(diào)用放在一起可以增強(qiáng)可讀性的話,我們也很難拒絕使用 block 來進(jìn)行處理。在用例之中,使用 block 來做完成的回調(diào),錯(cuò)誤的回調(diào),或者類似的事情,是很常見的情況。

Target-Action

Target-Action 是回應(yīng) UI 事件時(shí)典型的消息傳遞方式。iOS 上的 UIControl 和 Mac 上的 NSControl/NSCell 都支持這個(gè)機(jī)制。Target-Action 在消息的發(fā)送者和接收者之間建立了一個(gè)松散的關(guān)系。消息的接收者不知道發(fā)送者,甚至消息的發(fā)送者也不知道消息的接收者會(huì)是什么。如果 target 是 nil,action 會(huì)在響應(yīng)鏈 (responder chain) 中被傳遞下去,直到找到一個(gè)響應(yīng)它的對(duì)象。在 iOS 中,每個(gè)控件甚至可以和多個(gè) target-action 關(guān)聯(lián)。

基于 target-action 傳遞機(jī)制的一個(gè)局限是,發(fā)送的消息不能攜帶自定義的信息。在 Mac 平臺(tái)上 action 方法的第一個(gè)參數(shù)永遠(yuǎn)接收者。iOS 中,可以選擇性的把發(fā)送者和觸發(fā) action 的事件作為參數(shù)。除此之外就沒有別的控制 action 消息內(nèi)容的方法了。

做出正確的選擇

基于上述對(duì)不同消息傳遞機(jī)制的特點(diǎn),我們畫了一個(gè)流程圖來幫助我們?cè)诓煌榫诚伦龀霾煌倪x擇。一句忠告:流程圖的建議不代表最終答案。有些時(shí)候別的選擇依然能達(dá)到應(yīng)有的效果。只不過大多數(shù)情況下這張圖能引導(dǎo)你做出正確的決定。

http://wiki.jikexueyuan.com/project/objc/images/7-3.png" alt="" />

圖中有些細(xì)節(jié)值得深究:

有個(gè)框中說到: 發(fā)送者支持 KVO。這不僅僅是說發(fā)送者會(huì)在值改變的時(shí)候發(fā)送 KVO 通知,而且說明觀察者需要知道發(fā)送者的生命周期。如果發(fā)送者被存在一個(gè) weak 屬性中,那么發(fā)送者有可能會(huì)自己變成 nil,那時(shí)觀察者會(huì)導(dǎo)致內(nèi)存泄露。

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

最后在右下角的地方,一個(gè)選擇分支這樣說:發(fā)送者能確保釋放對(duì) block 的引用嗎?這涉及到了我們之前討論 block 的 API 存在潛在的 retain 環(huán)的問題。如果發(fā)送者不能保證在某個(gè)時(shí)間點(diǎn)會(huì)釋放對(duì) block 的引用,那么你會(huì)惹上 retain 環(huán)的麻煩。

Framework 示例

本節(jié)我們通過一些蘋果框架里的例子來驗(yàn)證流程圖的選擇是否有道理,同時(shí)解釋為什么蘋果會(huì)選擇用這些機(jī)制。

KVO

NSOperationQueue 用了 KVO 觀察隊(duì)列中的 operation 狀態(tài)屬性的改變情況 (isFinished,isExecuting,isCancelled)。當(dāng)狀態(tài)改變的時(shí)候,隊(duì)列會(huì)收到 KVO 通知。為什么 operation 隊(duì)列要用 KVO 呢?

消息的接收者(operation 隊(duì)列)知道消息的發(fā)送者(operation),并 retain 它并控制后者的生命周期。另外,在這種情況下只需要單向的消息傳遞機(jī)制。當(dāng)然如果考慮到 oepration 隊(duì)列只關(guān)心那些改變 operation 的值的改變情況的話,就還不足以說服大家使用 KVO 了。但我們可以這么理解:被傳遞的消息可以被當(dāng)成值的改變來處理。因?yàn)?state 屬性在 operation 隊(duì)列以外也是有用的,所以這里適合用 KVO。

http://wiki.jikexueyuan.com/project/objc/images/7-4.png" alt="" />

當(dāng)然 KVO 不是唯一的選擇。我們也可以將 operation 隊(duì)列作為 operation 的 delegate 來使用,operation 會(huì)調(diào)用類似 operationDidFinish: 或者 operationDidBeginExecuting: 等方法把它的 state 傳遞給 queue。這樣就不太方便了,因?yàn)?operation 要保存 state 屬性,以便于調(diào)用這些 delegate 方法。另外,由于 queue 不能主動(dòng)獲取 state 信息,所以 queue 也必須保存所有 operation 的 state。

Notifications

Core Data 使用 notification 傳遞事件(例如一個(gè) managed object context 中的改變————NSManagedObjectContextObjectsDidChangeNotification

發(fā)生改變時(shí)觸發(fā)的 notification 是由 managed object contexts 發(fā)出的,所以我們不能假定消息的接收者知道消息的發(fā)送者。因?yàn)橄⒌脑搭^不是一個(gè) UI 事件,很多接收者可能在關(guān)注著此消息,并且消息傳遞是單向的,所以 notification 是唯一可行的選擇。

http://wiki.jikexueyuan.com/project/objc/images/7-5.png" alt="" />

Delegation

Table view 的 delegate 有多重功能,它可以從管理 accessory view,直到追蹤在屏幕上顯示的 cell。例如我們可以看看 tableView:didSelectRowAtIndexPath: 方法。為什么用 delegate 實(shí)現(xiàn)而不是 target-action 機(jī)制?

正如我們?cè)谏鲜隽鞒虉D中看到的,用 target-action 時(shí),不能傳遞自定義的數(shù)據(jù)。而選中 table view 的某個(gè) cell 時(shí),collection view 不僅需要告訴我們一個(gè) cell 被選中了,也要通過 index path 告訴我們哪個(gè) cell 被選中了。如果我們照著這個(gè)思路,流程圖會(huì)引導(dǎo)我們使用 delegation 機(jī)制。

http://wiki.jikexueyuan.com/project/objc/images/7-6.png" alt="" />

如果不在消息傳遞中包含選中 cell 的 index path,而是讓選中項(xiàng)改變時(shí)我們像 table view 主動(dòng)詢問并獲取選中 cell 的相關(guān)信息,會(huì)怎樣呢?這會(huì)非常不方便,因?yàn)槲覀儽仨氂涀‘?dāng)前選中項(xiàng)的數(shù)據(jù),這樣才能在多選擇中知道哪些 cell 是被新選中的。

同理,我們可以想象通過觀察 table view 選中項(xiàng)的 index path 屬性,當(dāng)該值發(fā)生改變的時(shí)候,獲得一個(gè)選中項(xiàng)改變的通知。不過我們會(huì)遇到上述相似問題:不做記錄的話我們就不能分辨哪一個(gè) cell 被選擇或取消選擇了。

Block

我們用 -[NSURLSession dataTaskWithURL:completionHandler:] 來作為一個(gè) block API 的介紹。那么從 URL 加載部分返回給調(diào)用者是怎么傳遞消息的呢?首先,作為 API 的調(diào)用者,我們知道消息的發(fā)送者,但是我們并沒有 retain 它。另外,這是個(gè)單向的消息傳遞————它直接調(diào)用 dataTaskWithURL: 的方法。如果我們對(duì)照流程圖,會(huì)發(fā)現(xiàn)這屬于 block 消息傳遞機(jī)制。

http://wiki.jikexueyuan.com/project/objc/images/7-7.png" alt="" />

有其他的選項(xiàng)嗎?當(dāng)然,蘋果自己的 NSURLConnection 就是最好的例子。NSURLConnection在 block 問世之前就存在了,所以它并沒有用 block 來實(shí)現(xiàn)消息傳遞,而是使用 delegation 來完成。當(dāng) block 出現(xiàn)以后,蘋果就在 OS X 10.7 和 iOS 5 平臺(tái)上的 NSURLConnection 中加了 sendAsynchronousRequest:queue:completionHandler:,所以我們不再在簡(jiǎn)單的任務(wù)中使用 delegate 了。

因?yàn)?NSURLSession 是個(gè)最近在 OS X 10.9 和 iOS 7 才出現(xiàn)的 API,所以它們使用 block 來實(shí)現(xiàn)消息傳遞機(jī)制(NSURLSession 有一個(gè) delegate,但是是用于其他目的)。

Target-Action

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

http://wiki.jikexueyuan.com/project/objc/images/7-8.png" alt="" />

如果 target 是明確指定的,那么 action 消息會(huì)發(fā)送給指定的對(duì)象。如果 target 是 nil, action 消息會(huì)一直在響應(yīng)鏈中被傳遞下去,直到找到一個(gè)能處理它的對(duì)象。在這種情況下,我們有一個(gè)完全解耦的消息傳遞機(jī)制:發(fā)送者不需要知道接收者,反之亦然。

Target-action 機(jī)制非常適合響應(yīng) UI 的事件。沒有其他的消息傳遞機(jī)制能夠提供相同的功能。雖然 notification 在發(fā)送者和接收者的松散關(guān)系上最接近它,但是 target-action 可以用于響應(yīng)鏈——只有一個(gè)對(duì)象獲得 action 并響應(yīng),action 在響應(yīng)鏈中傳遞,直到能遇到響應(yīng)這個(gè) action 的對(duì)象。

總結(jié)

一開始接觸這么多的消息傳遞機(jī)制的時(shí)候,我們可能有些無所適從,覺得所有的機(jī)制都可以被選用。不過一旦我們仔細(xì)分析每個(gè)機(jī)制的時(shí)候,它們各自都有特殊的要求和能力。

文中的選擇流程圖是幫助你清楚認(rèn)識(shí)這些機(jī)制的好的開始,當(dāng)然它不是所有問題的答案。如果你覺得這和你自己選擇機(jī)制的方式相似或是有任何缺漏,歡迎來信指正。

上一篇:Android下一篇:Android Intents