OC-RunTime 傳送訊息過程(一)
阿新 • • 發佈:2018-12-21
廢話少說,直接上乾貨。
1、首先用X-code建立一個工程選擇->macOS->Command Line Tool->填寫工程名稱oc -info。
2、建立完成後會有一個main.m檔案,在檔案內看到如下程式碼:
#import <Foundation/Foundation.h>
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
3、我們在這個main.m建立一個Person類。並且在main中初始化Person、給屬性賦值、呼叫方法。程式碼如下:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic,copy)NSString *name; - (void)showMyInfo; @end @implementation Person - (void)showMyInfo{ NSLog(@"showMyInfo"); } @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [Person alloc]; p = [p init]; p.name = @"zhang san"; [p showMyInfo]; } return 0; }
Person初始化分為兩部,是為了後面通過clang 來檢視每個步驟呼叫的具體實現。
首先我們利用終端cd 到當前main.m檔案同目錄下。然後執行下面clang 語句
clang -rewrite-objc main.m
該命令可以將.m的OC檔案轉寫為.cpp檔案。
在工程目錄下找到main.cpp檔案並開啟。會看到如下程式碼片段:
int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")); p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("init")); ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)p, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_pc_kltvgylx09l_qc2x15njv2wh0000gp_T_main_08433e_mi_1); ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("showMyInfo")); } return 0; }
初始化Person當呼叫alloc 方法時候轉化c語言程式碼
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
這一行程式碼做了三件事情,第一獲取Person
類,第二註冊alloc
方法,第三傳送訊息,將訊息alloc
傳送給類物件,可以簡單的將註冊方法理解為,通過方法名獲取到轉寫後C語言函式的函式指標。
當呼叫init 方法時程式碼如下
p = objc_msgSend(p, sel_registerName("init"));
註冊了init
方法,然後通過objc_msgSend
函式將訊息init
傳送給訊息的接受者p
。
//這一行是用來查詢引數的地址,取名為name
(NSString *)&__NSConstantStringImpl__var_folders_pc_kltvgylx09l_qc2x15njv2wh0000gp_T_main_08433e_mi_1
objc_msgSend(p, sel_registerName("setName:"), name)
和上面類似,註冊一個setName:方法、傳遞一個引數。再利用objc_msgSend 傳送給訊息接受者p.
呼叫方法和上面方式一樣如下:
objc_msgSend(p, sel_registerName("showMyInfo"));
可以看出OC的runtime
通過objc_msgSend
函式將一個面向物件的訊息傳遞轉為了面向過程的函式呼叫。