iOS進階—Runtime:OC方法底層呼叫過程
阿新 • • 發佈:2018-12-04
GitHub參考
PS:參考GitHub分享的Runtime002程式碼
OC方法底層呼叫過程
如果檢視OC的底層呼叫過程,我們需要藉助clang工具
使用終端
cd 專案目錄
clang -rewrite-objc main.m
會生成一個main.cpp
檔案
新增進專案當中,注意不要選擇編譯選項,如下圖
我們檢視main.cpp檔案當中main函式的實現
我們把其中關注的程式碼複製過來,如下((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("walk"));
[p walk];
是等價的。
#import <Foundation/Foundation.h> #import "TZPerson.h" #import <objc/message.h> int main(int argc, const char * argv[]) { @autoreleasepool { TZPerson * p =[TZPerson new]; // [p walk]; ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("walk")); } return 0; }
把上面的強轉程式碼刪除,對其簡化如下:objc_msgSend(p, sel_registerName("walk"));
注意:去掉強轉型別,需要做如下設定,編譯預處理時,去掉嚴格審查功能,才能執行
我們編寫程式碼NSLog(@"%p,%p",@selector(walk),sel_registerName("walk"));
檢視,輸出
2018-11-16 15:23:41.477798+0800 Runtime001[4290:154325] 0x100000f9d,0x100000f9d 2018-11-16 15:23:41.478135+0800 Runtime001[4290:154325] -[TZPerson walk] Program ended with exit code: 0
可以看到兩種程式碼的執行地址是一模一樣的,所以得出結論sel_registerName("walk"))
= @selector(walk)
其實類訊息傳送也是一樣的,程式碼如下:
#import <Foundation/Foundation.h>
@interface TZPerson : NSObject
- (void) walk;
+ (void) run;
@end
[TZPerson run];
objc_msgSend(objc_getClass("TZPerson"), @selector(run));
所以,我們得出結論:OC方法呼叫的本質msg_Send
,就是需要訊息接收者objc_getClass("TZPerson")
和訊息名稱@selector(run)