iOS 知識整理 Runtime

2021-10-02 18:43:21 字數 4494 閱讀 9784

目錄

runtime--什麼是runtime

runtime--概念解析

runtime--字典轉模型

runtime--動態新增屬性

runtime--動態新增方法

runtime--交換方法

runtime--方法呼叫流程

runtime--動態訊息解析

runtime簡稱執行時,oc就是執行時機制,也就是在執行時候的一些機制,其中最主要的是訊息機制。

runtime是一套底層純c語言api,oc**最終都會被編譯器轉化為執行時**,通過訊息機制決定函式呼叫方式,這也是oc作為動態語言使用的基礎

需要runtime的場合:需要呼叫私有方法的時候。

1.objc_msgsend

所有 objective-c方法呼叫在編譯時都會轉化為c函式objc_msgsend的呼叫。objc_msgsend(receiver,selector);是[receiver selector];對應的c函式

2.object(物件)

在objc/runtime.h中,object(物件)被定義為 指向object_object結構體的指標,objc_object結構體的資料結構如下:

struct objc_object

//id是乙個指向objc_object結構體的指標,即在runtime中:

typedef struct objc_object *id;

//oc中的物件雖然沒有明顯的使用指標,但是在oc**被編譯轉化為c之後,每個oc物件其實都是擁有乙個isa的指標

3.class(類)

在objc/runtime.h中,class(類)被定義為指向objc_class結構體的指標,objc_class結構體的資料結構如下:

struct objc_class;

//class 是乙個指向objc_class結構體的指標,即在runtime中:

typedef struct objc_class *class;

4.sel(方法選擇器)

在objc/runtime.h中,sel(方法選擇器)被定義為指向objc_selector結構體的指標:

typedef struct objc_selector *sel;

struct objc_selector

//objective-c在編譯時,會依據每乙個方法的名字、引數序列,生成乙個唯一的整型標識(int型別的位址)

sel 可以將其理解為方法的id。

imp 可以理解為函式指標,指向了最終的實現。

sel 和 imp 的關係非常類似於 hashtable 中 key 與 value 的關係。oc 中不支援函式過載的原因就是因為乙個類的方法列表中不能存在兩個相同的 sel。但是多個方法卻可以在不同的類中有乙個相同的 sel ,不同類的例項物件執行相同的 sel 時 ,會在各自的方法列表中去根據 sel 去尋找自己對應的 imp。這使得 oc 可以支援函式重寫。

注意:1.不同類中相同名字的方法對應的方法選擇器是相同的。

2.即使是同乙個類中,方法名相同爾變數型別不同也會導致它們具有相同的方法選擇器。

通常獲取sel有三種方式:

1.oc中,使用@selector("方法名字串")

2.oc中,使用nsselectorfromstring("方法名字串")

3.在runtime方法,使用sel_registername("方法名字串")

5.ivar

在 objc/runtime.h 中,ivar 被定義為 指向 objc_ivar 結構體的指標,objc_ivar 結構體的資料結構如下:

struct obj_ivar

//ivar代表類中例項變數的型別,是乙個指向 object_ivar 的結構體的指標

typedef struct objc_ivar *ivar;

在objc_class中看到 ivars 成員列表,其中的元素就是ivar,可以通過例項查詢其在類中的名字,這個過程被稱為反射,

下面的class_copyivarlist獲取的不僅有例項變數還有屬性:

ivar *ivarlist = class_copyivarlist([self class], &count);

for (int i = 0;i6.method(方法)

在 objc/runtime.h中,method(方法)被定義為指向 objc_method 結構體的指標,

在 objc_class 定義中看到 methodlists,其中的元素就是 method,objc_method結構體的資料結構如下:

struct objc_method;

//method 表示某個方法的型別

typedef struct objc_method *method;

kvc底層原理(setvalue:forkey:)

1.先去模型中查詢有沒有setsource,找到直接賦值

2.去模型中查詢有沒有source屬性,有直接訪問屬性賦值 source=value

3.去模型中查詢有沒有_source屬性,有直接訪問屬性賦值,_source=value

4.找不到,就會直接報錯 setvalue:forunderfinedkey報找不到的錯誤

//重寫系統方法的目的:1.給系統方法新增額外功能。2.不想要系統方法實現

//重寫此方法可以不報錯

- (void)setvalue:(id)value forunderfinedkey:(nsstring *)key

runtime:根據模型中屬性,去字典中取出對應的value給模型屬性賦值

- (void)modewithdict:(nsdictonary *)dict 

return [super resolveinstancemethod:sel];

}//class:新增哪個方法

//sel:新增哪個方法

//imp:函式名

//types:方法型別"v@:"

class_addmethod(class,sel,imp,types)

只想修改系統的方法實現時候使用交換方法

1.給系統的方法新增分類

2.自己實現乙個帶有擴充套件功能的方法

1.通過isa去對應的類中查詢

2.註冊方法編號

3.根據方法編號去查詢對應方法

4.找到只是最終函式實現位址,根據位址去方法區呼叫對應函式

1.通過 resolveinstancemethod 得知方法是否為動態新增,yes則通過 class_addmethod 動態新增方法,處理訊息,否則進入下一步。dynamic 屬性就與這個過程有關,當乙個屬性宣告為 dynamic 時就是告訴編譯器:開發者一定會新增 setter/getter 的實現,而編譯時不用自動生成。

2.這步會進入 forwardingtargetforselector 用於指定哪個物件來響應訊息。如果返回 nil 則進入第三步。這種方式把訊息原封不動地**給目標物件,有著比較高的效率。如果不能自己的類裡面找到替代方法,可以過載這個方法,然後把訊息轉給其他的物件。

3.這步調用 methodsignatureforselector 進行方法簽名,這可以將函式的引數型別和返回值封裝。如果返回 nil 說明訊息無法處理並報錯 unrecognized selector sent to instance,如果返回 methodsignature,則進入 forwardinvocation,在這裡可以修改實現方法,修改響應物件等,如果方法呼叫成功,則成功。如果依然不能正確響應訊息,則報錯 unrecognized selector sent to instance。

總結:第一階段意義在於動態新增方法實現,第二階段直接把訊息**給其他物件,第三階段是對第二階段的擴充,可以實現多次**,**給多個物件等。這也許就是設計這三個階段的意義。

另外,乙個物件通過訊息**來響應一條訊息,「看起來像」繼承了在其他類定義的方法實現,這就變相實現了多繼承。

iOS開發 runtime機制

runtime簡稱執行時。就是系統在執行的時候的一些機制,其中最主要的是訊息機制。對於c語言,函式的呼叫在編譯的時候會決定呼叫哪個函式 c語言的函式呼叫請看這裡 編譯完成之後直接順序執行,無任何二義性。oc的函式呼叫成為訊息傳送。屬於動態呼叫過程。在編譯的時候並不能決定真正呼叫哪個函式 事實證明,在...

IOS高階開發 Runtime(三)

11 系統類的方法實現部分替換 void methodexchange 列印結果 仔細看log 2013 07 26 16 33 22.776 highoc 7104 c07 sssaaaass 2013 07 26 16 33 22.778 highoc 7104 c07 sssaaaass 12...

IOS高階開發 Runtime(三)

11 系統類的方法實現部分替換 void methodexchange 列印結果 仔細看log 2013 07 26 16 33 22.776 highoc 7104 c07 sssaaaass 2013 07 26 16 33 22.778 highoc 7104 c07 sssaaaass 12...