1. 程式人生 > >OC-RunTime 傳送訊息過程(一)

OC-RunTime 傳送訊息過程(一)

廢話少說,直接上乾貨。

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函式將一個面向物件的訊息傳遞轉為了面向過程的函式呼叫。