日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

为什么objc_msgSend必须用汇编实现

發布時間:2023/12/10 编程问答 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么objc_msgSend必须用汇编实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

譯者前言

?

總是看到有人說用匯編實現objc_msgSend是為了速度快,當然這個不可否認。但是難道沒有別的原因?于是就看到了這篇文章,遂翻譯之!=。=

?

我自己的理解就是,用匯編實現,是為了應對不同的“Calling convention”,把函數調用前的棧和寄存器的參數、狀態設置,交給編譯器去處理。

?

先看看原文吧。

?

原作者: Ari Grant

原文鏈接: Why objc_msgSend Must be Written in Assembly

http://arigrant.com/blog/2014/2/12/why-objcmsgsend-must-be-written-in-assembly

?

開始

?

對于Objective-C來說,調用一個對象實例的方法,也叫作向這個對象實例“發送消息”,而每條“消息”,在編譯階段都會轉變為一次對objc_msgSend函數的調用,調用的參數不僅有原本消息的所有參數,還有消息的接收者receiver和對應的方法selector。舉個例子,下面的語句:

?

[receiver message:foo beforeDate:bar];

?

將會被編譯成:

?

objc_msgSend(receiver, @selector(message:beforeDate:), foo, bar);

?

對于objc_msgSend函數的實現原理,前人已經做了大量的探索。所以,本文將會把重點放在objc_msgSend的一個之前沒有太受到關注的點上,那就是:

?

objc_msgSend是不可能用Objective-C、C或者C++實現的。

?

THE RETURN TYPE – 返回類型

?

先看看如下兩行代碼:

?

NSUInteger n = [array count];

id obj = [array objectAtIndex:6];

?

直觀上看,將會被編譯成

?

NSUInteger n = objc_msgSend(array,??@selector(count));

id obj = objc_msgSend(array, @selector(objectAtIndex:), 6);

?

但是實際上這是不可能的,因為沒有函數可以同時滿足這兩個調用。而且它的返回值也不能同時是NSUInteger和id。

?

而且,上面的代碼也是無法編譯通過的。那么,加上類型轉換怎么樣?

?

NSUInteger n = (NSUInteger (*)(id, SEL))objc_msgSend(array,??@selector(count));

id obj = (id (*)(id, SEL, NSUInteger))objc_msgSend(array, @selector(objectAtIndex:), 6);

?

這下可以編譯通過了,雖然看起來不直觀。。。

objc_msgSend是一個Public的函數,在里聲明,如果你想直接調用它,就必須按照上面的格式加上強制類型轉換,要不然是無法編譯通過的。但是objc_msgSend到底是如何實現,來支持各種返回類型的?本文后面會講到。

?

THE IMP – 方法對應的函數指針

?

objc_msgSend函數的本質很簡單,傳入一個接受者對象實例receiver和方法名selector,它就會按照以下步驟執行:(譯者注:只是最粗略的步驟=。=)

?

  • 獲取receiver得類Class

  • 在Class的方法列表method table里面查找對應selector的方法實現

  • 找到的話就調用,返回

  • 找不到就在其父類中找,重復前面的步驟(直到沒有父類為止)

?

整個流程很簡單,沿著繼承鏈,向上找到方法selector對應的函數指針即可,也就是IMP。同時,在每層Class中都有緩存,加快后續的方法查找。但是,這也只是objc_msgSend的實現細節,所以,接著往下看。

?

THE ARG TYPES AND COUNT – 參數類型和數量

?

簡單來說,當objc_msgSend找到對應的函數指針后,只要用傳入的參數調用這個函數即可。剩下來的就是找到一種方法,可以調用任意參數類型、數量的任意函數。

?

參數的數量很容易計算。然后我們可以把所有的參數都放入varargs,然后調用函數時傳入即可。但是這樣的話,每個Objective-C的方法都必須在其prologue(譯者注:函數執行具體的“任務”前,所做的準備環節)里面把所有的參數從varargs里面提取出來。

?

這種把參數打包到varargs里面然后又取出來的辦法顯然是非常糟糕的,同時也是不必要的。

?

在C語言中,調用一個函數會被編譯成對應的匯編語言指令,首先是設置參數(把參數放到寄存器、棧上),然后用如jump或者call的指令,跳到具體的函數代碼地址處。如果我們想支持任意類型的函數類型,我們就必須寫一個switch語句,把所有的參數組合情況都包含起來,這樣才能正確的為任何形式的函數設置參數(譯者注:即按照某種“規范”、“約定”,把參數依次存放到“約定”的寄存器、棧上),這顯然是沒有擴展性的,更是不可能的。

?

UNWINDING THE CALL – 拆解調用

?

objc_msgSend的解決辦法,主要依據的是:當objc_msgSend被調用時,所有的參數已經被設置好了。

?

換一種方式來說,就是:在objc_msgSend開始執行時,棧幀(stack frame)的狀態、數據,和各個寄存器的組合形式、數據,跟調用具體的函數指針(IMP)時所需的狀態、數據,是完全一致的!

?

如下這行代碼:

?

id obj = objc_msgSend(array, @selector(objectAtIndex:), 6);

?

在調用objc_msgSend時,需要設置三個參數,分別是被調用方receiver、方法名selector和最后一個整型參數6。這和具體的方法函數IMP的參數順序、類型是完全一致的,也就是說,調用objc_msgSend前,設置的棧、寄存器的狀態、數據正是調用具體的方法函數時需要的狀態!

?

所以,當objc_msgSend找到要調用的函數實現IMP后,只需要把所有的對棧、寄存器的操作“倒”回到objc_msgSend執行開始的狀態(類似于函數執行完成return返回前,做的“收尾處理”工作一樣,即epilogue),直接jump/call到IMP函數指針對應的地址,執行指令即可,因為所有的參數已經被設置好了。

?

同時,當selector對應的IMP執行完成后,返回值也被正確的設置好了(在x86平臺上,返回值被設置到了指定的寄存器eax/rax里,在arm上,則是r0寄存器),所以,我們也不必擔心前文提到的不同類型的返回值問題了。

?

WRAP UP – 總結

?

把上面提到的所有解釋綜合起來,就是:在C語言里面調用函數,必須在編譯時就知道調用的“狀態”;而這些“狀態”在運行時是無法得出或正確處理的,所以必須往底層走,用匯編處理。(譯者注:這里不知道咋翻譯好=。=,原文是:calling a function in C requires the signature to be known for each call-site at compile-time;doing so at run-time is not possible and so one must drop down into assembly and party there instead.)

?

UPDATE – 后續

?

有人指出objc_msgSend有可能是用GCC的擴展方法__builtin_apply_args,__builtin_apply,和__builtin_return實現的。這也正指出了一個事實,就是這些builtins方法是非常有必要的,因為單靠語言本身無法實現這些功能。實現objc_msgSend所需要的技巧,也正是實現這些builtins方法所需要的技巧。本文的目的并不是非要將什么是真正的C、什么不是真正的C分個清楚,只是為了指出objc_msgSend特殊罷了。

?

譯者總結

?

開頭也說了,我的理解是:用匯編實現,是為了應對不同的“Calling convention”,把函數調用前的棧和寄存器的參數、狀態設置,交給編譯器去處理。

?

嗯,以后不要再說用匯編實現只是為了快了=。=

轉載于:https://www.cnblogs.com/fengmin/p/5619115.html

總結

以上是生活随笔為你收集整理的为什么objc_msgSend必须用汇编实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。