級別:★★☆☆☆
標籤:「objc_msgSend」「尾調用優化」「尾遞歸」
做者: WYW、MrLiuQ
審校: QiShare團隊php
這篇文章的出現原由於QiShare團隊對iOS 編寫高質量Objective-C代碼(二)中 (6、理解objc_msgSend(對象的消息傳遞機制))的激烈討論。
html
這篇文章將認真完全地分析 OC對objc_msgSend
的「尾調用優化」。同時歡迎路過的大神留言討論。
git
尾調用(
Tail Call
):某個函數的最後一步僅僅只是調用了一個函數(能夠是自身,能夠是另外一個函數)。
QiShare提醒:注意 「僅僅」 兩個字。github
// 尾調用:
- (NSInteger)funcA:(NSInteger)num {
/* Some codes... */
if (num = 0) {
return [self funcA:num];// 尾調用->自身
}
if (num > 0) {
return [self funcB:num];// 尾調用->函數funcB
}
return [self funcC:num];// 尾調用->函數funcC
}
複製代碼
正例解釋:funcA的最後一步僅僅調用了另外一個函數。不管是調用funcA、funcB仍是funcC都屬於尾調用。~(不論調用函數的位置在哪,只要最後一步僅僅調用一個函數就行)~微信
// 不是尾調用1:
- (NSInteger)funcA:(NSInteger)num {
NSInteger num = [self funcB:(num)];
return num;// 不是尾調用->最後一步是返回一個值,而不是調用一個函數
}
複製代碼
反例解釋:不是尾調用。由於最後一步是返回一個值,而不是僅僅調用一個函數函數
// 不是尾調用2:
- (NSInteger)funcA:(NSInteger)num {
return [self funcB:(num)] + 1;// 不是尾調用->緣由:最後一步不只調用了函數還有 +1 操做
}
複製代碼
反例解釋:不是尾調用。由於最後一步不只調用了函數還有 +1 操做優化
小編準備了一個demo:經過「斷點」和「當前內存狀況」查看有無尾調用優化spa
無優化Demo效果圖:3d
解釋: 這種場景下,每次函數調用一直在進棧,不斷申請棧空間,最後會棧溢出,最終致使崩潰。
空間複雜度O(n),時間複雜度O(n)。code
下面請看圖解:
優化Demo效果圖:
解釋: 這種場景下,每次函數調用一直在重用棧幀,不申請棧空間。
空間複雜度O(1),時間複雜度O(n)。
下面請看圖解:
此次討論原由於《Effective Objective-C 2.0》做者的原話:
若是某函數的最後一項操做是調用另一個函數,那麼就能夠運用「 尾調用優化 」
技術。編譯器會生成調轉至另外一函數所需的指令碼,並且不會向調用堆棧中推入新的「棧幀」
(frame stack)。只有當某函數的最後一個操做僅僅是調用其餘函數而不會將其返回值另做他用
時,才能執行「 尾調用優化 」
。
這項優化對objc_msgSend
很是關鍵,若是不這麼作的話,那麼每次調用Objective-C方法以前,都須要爲調用objc_msgSend函數準備「棧幀」,你們在「棧蹤影」(stack trace)中能夠看到這種「棧幀」。此外,若是不優化,還會過早地發生「棧溢出」(stack overflow)現象。
做者這一段歸納的話,很精簡。而小編第一次看時,感受很懵懂。在這裏,QiShare對這段話進行了詳細的分析:
尾調用優化的本質:很簡單,就是棧幀的複用。
尾調用優化的條件有三點:
函數調用的過程:函數調用會在內存中申請一塊「棧幀」,保存調用的地址和內部變量等信息。若是函數A內部調用函數B,那麼在函數A的棧幀上就會加上一個函數B的棧幀 。若是函數B再調用了函數C,那麼函數A的棧幀上就會有序加上函數B和函數C的棧幀。若是C運行結束了,返回到函數B,C的棧幀纔會消失。
4. 尾調用優化實現原理:當函數A的最後一步僅僅是調用另外一個函數B時(或者調用自身函數A),這時,由於函數A的位置信息和內部變量已經不會再用到了,直接把函數A的棧幀交給函數B使用。
總結:
1. 尾調用:某個函數的最後一步僅僅調用了一個函數(能夠是自身,能夠是另外一個函數)。
2. OC的尾調用優化的本質是:棧幀的複用
3. 尾調用優化實現原理:當函數A的最後一步僅僅是調用另外一個函數B時(或者調用自身函數A),這時,由於函數A的位置信息和內部變量已經不會再用到了,直接把函數A的棧幀交給函數B使用。
PS:尾調用優化在Release模式下才會有,Debug模式下沒有。
關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow) QiShare(微信公衆號)