OC 最實用的Runtime 總結

2021-07-24 12:13:17 字數 4515 閱讀 4250

什麼是runtime?

runtime 是 oc底層的一套c語言的api(引入 或),編譯器最終都會將oc**轉化為執行時**,通過終端命令編譯.m 檔案:clang -rewrite-objc ***.m可以看到編譯後的***.cpp(c++檔案)。

比如我們建立了乙個物件 [[nsobject alloc]init],最終被轉換為幾萬行**,擷取最關鍵的一句可以看到底層是通過runtime建立的物件

.cpp 檔案

刪除掉一些強制轉換語句,可以看到呼叫方法本質就是發訊息,[[nsobject alloc]init]語句發了兩次訊息,第一次發了alloc 訊息,第二次傳送init 訊息。利用這個功能我們可以**底層,比如block的實現原理。

需要注意的是,使用objc_msgsend() sel_registername()方法需要匯入標頭檔案

訊息機制

另外利用runtime 可以做一些oc不容易實現的功能

動態交換兩個方法的實現(特別是交換系統自帶的方法)

動態新增物件的成員變數和成員方法

獲得某個類的所有成員方法、所有成員變數

如何應用執行時?

1.將某些oc**轉為執行時**,**底層,比如block的實現原理(上邊已講到);

2.攔截系統自帶的方法呼叫(swizzle 黑魔法),比如攔截imagenamed:、viewdidload、alloc;

3.實現分類也可以增加屬性;

4.實現nscoding的自動歸檔和自動解檔;

5.實現字典和模型的自動轉換。

下面我通過demo 我乙個個來講解

一、交換兩個方法的實現,攔截系統自帶的方法呼叫功能

需要用到的方法

獲得某個類的類方法

method class_getclassmethod(class cls , sel name)

獲得某個類的例項物件方法

method class_getinstancemethod(class cls , sel name)

交換兩個方法的實現

void method_exchangeimplementations(method m1 , method m2)

案例1:方法簡單的交換

建立乙個person類,類中實現以下兩個類方法,並在.h 檔案中宣告

[person run];

[person study];

下面通過runtime 實現方法交換,類方法用class_getclassmethod ,物件方法用class_getinstancemethod

// 獲取兩個類的類方法

method m1 = class_getclassmethod([person class], @selector(run));

method m2 = class_getclassmethod([person class], @selector(study));

// 開始交換方法實現

method_exchangeimplementations(m1, m2);

// 交換後,先列印學習,再列印跑!

[person run];

[person study];

案例2:攔截系統方法

需求:比如ios6 公升級 ios7 後需要版本適配,根據不同系統使用不同樣式(擬物化和扁平化),如何通過不去手動乙個個修改每個uiimage的imagenamed:方法就可以實現為該方法中加入版本判斷語句?

步驟:1、為uiimage建乙個分類(uiimage+category)

2、在分類中實現乙個自定義方法,方法中寫要在系統方法中加入的語句,比如版本判斷

int _age;

需要用到的方法

set方法,將值value 跟物件object 關聯起來(將值value 儲存到物件object 中)

引數 object:給哪個物件設定屬性

引數 key:乙個屬性對應乙個key,將來可以通過key取出這個儲存的值,key 可以是任何型別:double、int 等,建議用char 可以節省位元組

引數 value:給屬性設定的值

引數policy:儲存策略 (assign 、copy 、 retain就是strong)

void objc_setassociatedobject(id object , const void key ,id value ,objc_associationpolicy policy)

利用引數key 將物件object中儲存的對應值取出來

id objc_getassociatedobject(id object , const void key)

步驟:1、建立乙個分類,比如給任何乙個物件都新增乙個name屬性,就是nsobject新增分類(nsobject+category)

2、先在.h 中@property 宣告出get 和 set 方法,方便點語法呼叫

@property(nonatomic,copy)nsstring *name;

3、在.m 中重寫set 和 get 方法,內部利用runtime 給屬性賦值和取值

char namekey;

需要用到的方法

獲得某個類的所有成員變數(outcount 會返回成員變數的總數)

引數:1、哪個類

2、放乙個接收值的位址,用來存放屬性的個數

3、返回值:存放所有獲取到的屬性,通過下面兩個方法可以調出名字和型別

ivar class_copyivarlist(class cls , unsigned int outcount)

獲得成員變數的名字

const char ivar_getname(ivar v)

獲得成員變數的型別

const char ivar_gettypeendcoding(ivar v)

案例1:獲取person類中所有成員變數的名字和型別

unsigned int outcount = 0;

ivar *ivars = class_copyivarlist([person class], &outcount);

// 遍歷所有成員變數

for (int i = 0; i < outcount; i++)

// 注意釋放記憶體!

free(ivars);

案例2:利用runtime 獲取所有屬性來重寫歸檔解檔方法

// 設定不需要歸解檔的屬性

// 解檔方法

// 歸檔呼叫方法

注意,下面的**我換了乙個方法名(不然會覆蓋系統原來的方法!),加了乙個忽略屬性方法是否被實現的判斷,並加上了對父類屬性的歸解檔迴圈。

nsobject+extension.h

@inte***ce nsobject (extension)

@end

nsobject+extension.m

@implementation nsobject (extension)

}// 設定需要忽略的屬性

// 在系統方法內來呼叫我們的方法

案例3:利用runtime 獲取所有屬性來進行字典轉模型

以往我們都是利用kvc進行字典轉模型,但是它還是有一定的侷限性,例如:模型屬性和鍵值對對應不上會crash(雖然可以重寫setvalue:forundefinedkey:方法防止報錯),模型屬性是乙個物件或者陣列時不好處理等問題,所以無論是效率還是功能上,利用runtime進行字典轉模型都是比較好的選擇。

字典轉模型我們需要考慮三種特殊情況:

1.當字典的key和模型的屬性匹配不上

2.模型中巢狀模型(模型屬性是另外乙個模型物件)

3.陣列中裝著模型(模型的屬性是乙個陣列,陣列中是乙個個模型物件)

根據上面的三種特殊情況,我們乙個個處理,先是字典的key和模型的屬性不對應的情況。

不對應有兩種,一種是字典的鍵值大於模型屬性數量,這時候我們不需要任何處理,因為runtime是先遍歷模型所有屬性,再去字典中根據屬性名找對應值進行賦值,多餘的鍵值對也當然不會去看了;另外一種是模型屬性數量大於字典的鍵值對,這時候由於屬性沒有對應值會被賦值為nil,就會導致crash,我們只需加乙個判斷即可,json資料和sample如下:

json資料

json資料

這時候我們就需要利用runtime的ivar_gettypeencoding 方法獲取模型物件型別,對該模型物件型別再進行字典轉模型,也就是進行遞迴,需要注意的是我們要排除系統的物件型別,例如nsstring,下面的方法中我新增了乙個類方法方便遞迴。

列印可以看到各屬性型別

@implementation nsobject (jsonextension)

json資料

我們既然能獲取到屬性型別,那就可以攔截到模型的那個陣列屬性,進而對陣列中每個模型遍歷並字典轉模型,但是我們不知道陣列中的模型都是什麼型別,我們可以宣告乙個方法,該方法目的不是讓其呼叫,而是讓其實現並返回模型的型別。

這塊語言可能解釋不太清楚,可以參考我的demo,直接執行即可。

nsobject+jsonextension.h

// 返回陣列中都是什麼型別的模型物件

@implementation nsobject (jsonextension)

@end

原文:

OC 的 Runtime 動態獲取物件屬性

執行時是 oc 的核心,其特點就是程式在執行的時候,以傳送訊息的形式呼叫方法.在實際開發中,關於執行時方面的運用點,其實並不多,最常用的也無非是動態獲取物件的屬性.示例 1.新建乙個繼承自nsobject 的物件,在.h 檔案宣告2個屬性 import inte ceperson nsobject ...

linux最實用的命令

1 檢視程序服務數,例如 ps u ats3 grep commer wc l 其中 u 後面跟使用者,grep commer 是看ats3使用者下的commer服務數,wc l 看行數 2 按時間排序檢視檔案 ls lrt 3 vi中的命令 set nu 顯示行數 按x鍵 刪除游標處的乙個字元 按...

python 最實用的排序

1.列表去重,根據原有的順序排序order list 上海 az1 上海 az1 上海 az1 上海 az1 上海 az2 上海 az2 上海 az2 北京 az1 order info list set order list order info.sort key order list.index...