1. 程式人生 > >iOS進階—Runtime:OC方法底層呼叫過程

iOS進階—Runtime:OC方法底層呼叫過程

GitHub參考
PS:參考GitHub分享的Runtime002程式碼

iOS進階—目錄


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)