Runtime 知識整理

一、如何在項目中使用runtime

第一步: 在需要使用的類中導入文件

#import

第二步: 在Build Setting中搜索msg,

二、本篇主要講述了以下幾種runtime用法,有一定基礎用于提醒自己的玩家可以直接看表格,其他玩家請繼續(xù)往下看:

用法關(guān)鍵函數(shù)

動態(tài)獲取類名const char *class_getName(Class cls)

動態(tài)獲取類的成員變量Ivar *class_copyIvarList(Class cls, unsigned int *outCount)、

const char *ivar_getName(Ivar v)、

const char *ivar_getTypeEncoding(Ivar v)

動態(tài)獲取類的屬性列表objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)、

const char *property_getName(objc_property_t property)

動態(tài)獲取類的實例方法列表Method *class_copyMethodList(Class cls, unsigned int *outCount)、

SEL method_getName(Method m)

動態(tài)獲取類所遵循的協(xié)議列表Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)、

const char *protocol_getName(Protocol *p)

動態(tài)添加新的方法Method class_getInstanceMethod(Class cls, SEL name)、

IMP method_getImplementation(Method m)、

const char *method_getTypeEncoding(Method m)、

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

類的實例方法實現(xiàn)交換Method class_getInstanceMethod(Class cls, SEL name)、

void method_exchangeImplementations(Method m1, Method m2)

動態(tài)屬性關(guān)聯(lián)void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)、

id objc_getAssociatedObject(id object, const void *key)

消息發(fā)送與消息轉(zhuǎn)發(fā)機制+ (BOOL)resolveInstanceMethod:(SEL)sel、

- (id)forwardingTargetForSelector:(SEL)aSelector、

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector、

- (void)forwardInvocation:(NSInvocation *)anInvocation

我們首先封裝一個測試類TestClass,其中需要包含遵守協(xié)議,并添加公有屬性、私有屬性、私有成員變量、公有實例方法、私有實例方法、類方法等。

這些內(nèi)容的添加主要便于之后的測試。

TestClass方法變量聲明.png

TestClass私有變量.png

TestClass方法實現(xiàn).png

一、動態(tài)獲悉類結(jié)構(gòu)

1. 動態(tài)獲取類名

采用class_getName(cls)在運行時獲取類的名稱。將char類型的指針轉(zhuǎn)換成NSString類型進行返回。

獲取類名.png

2. 動態(tài)獲取成員變量

采用class_copyIvarList(cls, count)獲取成員變量列表。使用ivar_getName(variable)來輸出成員變量名稱,ivar_getTypeEncoding(variable)來輸出成員變量類型。

我們通過將所得數(shù)據(jù)組合成NSDictionary來存儲單個變量,若干個字典組成NSArray作為屬性列表的返回。

獲取成員變量.png

使用TestClass進行用例測試。由于是調(diào)用上述方法獲取TestClass的成員變量,到了運行時階段實際就不存在公有私有之分。OC中的類在ARC情況下添加的屬性,其實就是自動生成其get方法與set方法。

所有獲取的成員列表中肯定帶有成員屬性,成員屬性的名稱前方帶有下劃線用于成員變量進行區(qū)分。

下方中各基本類型由特殊字母代替,可以看出i代表int類型,c代表bool類型,d表示double類型,f表示float類型。而如果是引用類型則直接是一個字符串顯示,比如NSString類型就是@"NSString"。

測試成員列表打印.png

3. 動態(tài)獲取成員屬性列表

上方獲取了類的成員變量,那么下方進行屬性列表的獲取。屬性區(qū)分于變量主要是它們擁有完整的set方法和get方法。

我們使用class_copyPropertyList(cls, count)來獲取屬性列表,通過property_getName(property)來獲取屬性名稱。

獲取屬性列表.png

下方dynamic的屬性是我們使用runtime進行動態(tài)添加的。

測試屬性列表打印.png

4. 獲取類的實例方法

我們通過class_copyMethodList(cls, count)來獲取實例方法列表,通過method_getName(method)來獲取實例方法名稱。

獲取實例方法.png

下方打印了所有TestClass類的實例方法,當然包括成員屬性的set方法和get方法。其中.cxx_destruct方法不確認歸屬于何處,也許dealloc方法的自我實現(xiàn)?

測試實例方法列表打印.png

5. 獲取類的協(xié)議列表

我們使用class_copyProtocolList(cls, count)來獲取協(xié)議列表,使用protocol_getName(protocol)來獲取協(xié)議名稱

獲取協(xié)議列表.png

二、動態(tài)操作類方法

1. 動態(tài)添加方法實現(xiàn)

其添加原理旨在使用class_getInstanceMethod(cls, methodName)獲取相關(guān)的方法聲明以及使用method_getImplementation(method)獲取相關(guān)的方法實現(xiàn)。將它們進行組合后,使用class_addMethod(cls, methodName, method, type)進行方法的添加。

動態(tài)添加方法實現(xiàn).png

2. 實現(xiàn)方法交換

通過class_getInstanceMethod(cls, methodName)獲取到需要交換的兩個方法,直接使用method_exchangeImplementation(methodA, methodB)進行方法替換即可。

實現(xiàn)方法交換.png

通過類目為測試類封裝一個針對交換方法的測試用例。

如果是普通情況下,沒有交換。在replaceMethod中調(diào)用本身勢必會造成死循環(huán)。

如是如果交換方法成功,那么此時在replaceMethod中調(diào)用replaceMethod,其實此時調(diào)用的是exchangeMethodA。由于exchangeMethodA不存在死循環(huán),故在測試時,調(diào)用了封裝的交換方法后,進一步又調(diào)用了replaceMethod,其實只是調(diào)用了exchangeMethodA而已。

交換方法的封裝.png

三、屬性關(guān)聯(lián)

屬性關(guān)聯(lián)可以說是runtime最普通的打開方式了。通過為屬性聲明一個靜態(tài)名稱,調(diào)用void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)實現(xiàn)新增屬性的set方法,調(diào)用id objc_getAssociatedObject(id object, const void *key)實現(xiàn)新增屬性的get方法即可。

動態(tài)添加屬性.png

四、消息處理與消息轉(zhuǎn)發(fā)

消息處理過程:

當你調(diào)用一個類的方法時,先在本類中的方法緩存列表中進行查詢,如果在緩存列表中找到了該方法的實現(xiàn),就執(zhí)行;如果找不到就在本類中的方法列表中進行查找。

在本類方法列表中查找到相應的方法實現(xiàn)后就進行調(diào)用,如果沒找到,就去父類中進行查找;如果在父類中的方法列表中找到了相應方法的實現(xiàn)。

當在方法緩存列表,本類中的方法列表以及父類中的方法列表中都找不到相應的實現(xiàn),到程序崩潰以前還會經(jīng)歷以下過程:

消息處理

如果一直尋找方法直到父類中都找不到方法實現(xiàn)時會執(zhí)行+ (BOOL)resolveInstanceMethod:(SEL)sel類方法。

如果返回NO,則表明不做任何處理,繼續(xù)下一步。如果返回YES,就說明該方法中對找不到實現(xiàn)的方法進行了處理。

我們就可以在此方法中為找不到實現(xiàn)的SEL動態(tài)添加一個方法實現(xiàn),添加完畢后,就會執(zhí)行我們添加的方法實現(xiàn)。

下一次程序再找不到該類某個方法的實現(xiàn)時,就不會因為找不到而崩潰了。

消息處理.png

2.消息轉(zhuǎn)發(fā)

如果不對上述消息進行處理的話,也就是+ (BOOL)resolveInstanceMethod:(SEL)sel方法返回NO時。便進入了下一步消息轉(zhuǎn)發(fā)。

即執(zhí)行- (id)forwardingTargetForSelector:(SEL)aSelector方法。該方法會返回一個類對象,該類的對象有SEL對應的實現(xiàn),當調(diào)用這個找不到方法時,就會轉(zhuǎn)發(fā)到ExtClass中進行處理。

此時完成消息轉(zhuǎn)發(fā)。如果該方法返回self或者nil,說明不對相應的方法進行轉(zhuǎn)發(fā),那就再走下一步。

消息轉(zhuǎn)發(fā).png

3.消息常規(guī)轉(zhuǎn)發(fā)

如果不將消息轉(zhuǎn)發(fā)給其他類的對象,則此時代表自己進行處理。即上述的方法中返回self或者nil。

此時執(zhí)行- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector來獲取方法的參數(shù)以及返回數(shù)據(jù)類型,即可以理解為該方法的簽名。

如果此時再次返回nil,那么消息轉(zhuǎn)發(fā)結(jié)束。程序崩潰,報出找不到相應的方法實現(xiàn)的崩潰消息。

下方方法執(zhí)行的先決條件,是要在+ (BOOL)resolveInstanceMethod:(SEL)sel中返回NO。然后下方也是進行將方法轉(zhuǎn)給ExtClass的實現(xiàn)。

消息常規(guī)轉(zhuǎn)發(fā).png

本文項目Github鏈接地址:https://github.com/LibertyLeo/Runtime-Usage

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容