iOS底層原理探索-06- Runtime之方法的本質

《目錄-iOS & OpenGL & OpenGL ES & Metal》html

對象和類以前已經分析過了,今天來分析一下方法,說到方法就不得不探究一下Runtime了~c++

1、Runtime

一、Runtime的定義

Runtime是一套API。api

詳細來講,是一套由c、c++、彙編一塊兒編寫而成,併爲oc代碼提供運行時功能的api。markdown

爲何要這麼費勁巴拉的搞這麼一套東西,不直接用oc呢?app

  • 由於oc對於計算機來講是一門高級語言,而c、c++、彙編相比oc,更加穩定,執行效率也更快。整體來講是爲了穩定性和高效率!

運行時 & 編譯時iphone

  • 運行時:代碼跑起來的時間,代碼會被裝載在內存ide

  • 編譯時:正在編譯的時間,將源代碼翻譯成可識別的機器語言(如:二進制)函數

蘋果官方文檔-runtime相關oop

二、Runtime的版本

  • 老版本,legacy version, OC 1.0 ,__OBJC__,-old
  • 新版本,modern version, OC 2.0 ,__OBJC2__,-new

三、Runtime的使用

運行時動態庫 Runtime System Library 經過 編譯器 compiler編譯以後,提供了 Framework&ServiceRuntime API 供OC使用。post

咱們常常在OC中調用@selector(),其實就是在和runtime打交道了

  • OC上層方法 : @selector()
  • NSObject方法 : NSSelectorFromName
  • Runtime 底層api:sel_registerName

2、方法的本質

一、準備工做

測試代碼:

// Person是繼承自NSObject 自定義的類
Person *person = [Person alloc];

[person sayHello];


//定義一個void fly() {···},直接調用方法
fly();
複製代碼

經過clang手段:

兼容編譯(代碼少):clang -rewrite-objc main.m -o main.cpp

完整編譯(不報錯):xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
複製代碼

二、方法的底層編譯

看一下底層編譯成什麼:

Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc"));

((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));

fly();
複製代碼

解析:

  • ((Person *(*)(id, SEL))(void *)objc_msgSend) ((void (*)(id, SEL))(void *)objc_msgSend) 都是 類型強轉
  • (id)objc_getClass("Person")拿到Person類
  • sel_registerName("alloc")sel_registerName("sayHello") 都是 調用方法,比如@selector()
  • 定義的C函數fly()clang後,並無編譯成objc_msgSend函數去調用。由於發送消息就是查找函數實現的過程,C函數能夠直接經過函數名(指針)找到,並不須要這一步。

重點即:objc_msgSend(id,sel) = 發送消息(消息接收者,方法編號)

三、方法的本質

由底層編譯能夠看出,咱們調用方法,在底層會變成objc_msgSend函數的調用。也就是說:

方法的本質就是發送消息

四、給不一樣的接受者發消息

假定條件: 有一個子類 Person,實例方法:run ,類方法:jump 有一個父類 People,實例方法:sayHi,類方法:sayBey

4.1 給對象發消息
//person的角度
objc_msgSend(person, sel_registerName("run"));
複製代碼
4.2 給類發消息
//person的角度
objc_msgSend(objc_getClass("Person"), sel_registerName("jump"));
複製代碼
4.3 給父類發消息(實例方法)

給父類發消息,要用另外一個函數objc_msgSendSuper,而且要用到objc_super這個結構體

struct objc_super {
    //只看oc2版本須要的2個參數 
    __unsafe_unretained _Nonnull id receiver;  
    //··· 
    __unsafe_unretained _Nonnull Class super_class;
 	//··· 
};
複製代碼
//person 的角度
struct objc_super mySuper;
mySuper.receiver = person;
mySuper.super_class = [Person class];
objc_msgSendSuper(&mySuper, @selector(sayHi));
複製代碼
4.3 給父類發消息(類方法)
//person 的角度
struct objc_super myClassSuper;
myClassSuper.receiver = [Person class];
myClassSuper.super_class = class_getSuperclass(object_getClass([Person class]));// 元類
objc_msgSendSuper(&myClassSuper, sel_registerName("sayBey"));
複製代碼
相關文章
相關標籤/搜索