說道Objective-C里面的消息機制,大部分人都知道是調(diào)用方法其實就是發(fā)送消息缅糟,一個叫objc_msgSend的東西負(fù)責(zé)的盛龄。今天結(jié)合《編寫高質(zhì)量iOS與OS X代碼的52個有效方法》趴一趴消息機制航缀。
為什么Objective-C里會有消息機制
這就是語言的基因問題了Smalltalk夜牡,之前在一本叫《代碼的未來》了解到Smalltalk是一門比較古老的語言与纽,在 Smalltalk 中一切皆對象,一切調(diào)用都是發(fā)消息塘装。在它之前有Lisp 和 FORTRAN急迂、COBOL并稱為“古代編程語言三巨頭”。
Objective-C是在C的基礎(chǔ)上蹦肴,借鑒 Smalltalk 的面向?qū)ο笈c消息機制擴展出來的語言僚碎,就像Golang語言天生自帶并發(fā)基因。
發(fā)送消息的過程
在Objective-C中阴幌,如果向某個對象傳遞消息勺阐,那就會在運行時使用動態(tài)綁定(dynamic binding)機制來決定需要調(diào)用的方法。但是到了底層具體實現(xiàn)矛双,卻是普通的C語言函數(shù)實現(xiàn)的皆看。這個實現(xiàn)的函數(shù)就是objc_msgSend,該函數(shù)定義如下:
void objc_msgSend(id self, SEL cmd, ...)
這是一個參數(shù)個數(shù)可變的函數(shù),第一參數(shù)代表接收者背零,第二個參數(shù)代表選擇子(OC函數(shù)名),后續(xù)的參數(shù)就是消息(OC函數(shù)調(diào)用)中的那些參數(shù)
舉例來說:
id return = [git commit:parameter];
上面的Objective-C方法在運行時會轉(zhuǎn)換成如下函數(shù):
id return = objc_msgSend(git, @selector(commit), parameter);
objc_msgSend函數(shù)會在接收者所屬的類中搜尋其方法列表无埃,如果能找到這個跟選擇子名稱相同的方法徙瓶,就跳轉(zhuǎn)到其實現(xiàn)代碼,往下執(zhí)行嫉称。若是當(dāng)前類沒找到侦镇,那就沿著繼承體系繼續(xù)向上查找,等找到合適方法之后再跳轉(zhuǎn) 织阅,如果最終還是找不到壳繁,那就進入消息轉(zhuǎn)發(fā)的流程去進行處理了。
說過了OC的函數(shù)調(diào)用實現(xiàn)荔棉,你會覺得消息轉(zhuǎn)發(fā)要處理很多闹炉,尤其是在搜索上,幸運的是objc_msgSend在搜索這塊是有做緩存的润樱,每個OC的類都有一塊這樣的緩存渣触,objc_msgSend會將匹配結(jié)果緩存在快速映射表(fast map)中,這樣以來這個類一些頻繁調(diào)用的方法會出現(xiàn)在fast map 中壹若,不用再去一遍一遍的在方法列表中搜索了嗅钻。
還有一個有趣的點皂冰,就是在底層處理發(fā)送消息的時候,有用到尾調(diào)用優(yōu)化养篓,大概原理就是在函數(shù)末尾調(diào)用某個不含返回值函數(shù)時秃流,編譯器會自動的不在棧空間上重新進行分配內(nèi)存柳弄,而是直接釋放所有調(diào)用函數(shù)內(nèi)部的局部變量舶胀,然后直接進入被調(diào)用函數(shù)的地址。
寫在最后
《編寫高質(zhì)量iOS與OS X代碼的52個有效方法》是一本很不錯的書语御,想在OC做的深的一點話峻贮,最好看看這本書,必會有不少收益