一瓢谢、如何在項(xiàng)目中使用runtime
第一步: 在需要使用的類(lèi)中導(dǎo)入文件
#import
第二步: 在Build Setting中搜索msg,
二呛牲、本篇主要講述了以下幾種runtime用法,有一定基礎(chǔ)用于提醒自己的玩家可以直接看表格,其他玩家請(qǐng)繼續(xù)往下看:
用法關(guān)鍵函數(shù)
動(dòng)態(tài)獲取類(lèi)名const char *class_getName(Class cls)
動(dòng)態(tài)獲取類(lèi)的成員變量Ivar *class_copyIvarList(Class cls, unsigned int *outCount)韩容、
const char *ivar_getName(Ivar v)扣唱、
const char *ivar_getTypeEncoding(Ivar v)
動(dòng)態(tài)獲取類(lèi)的屬性列表objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)藕坯、
const char *property_getName(objc_property_t property)
動(dòng)態(tài)獲取類(lèi)的實(shí)例方法列表Method *class_copyMethodList(Class cls, unsigned int *outCount)、
SEL method_getName(Method m)
動(dòng)態(tài)獲取類(lèi)所遵循的協(xié)議列表Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)噪沙、
const char *protocol_getName(Protocol *p)
動(dòng)態(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)
類(lèi)的實(shí)例方法實(shí)現(xiàn)交換Method class_getInstanceMethod(Class cls, SEL name)辐马、
void method_exchangeImplementations(Method m1, Method m2)
動(dòng)態(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ā)機(jī)制+ (BOOL)resolveInstanceMethod:(SEL)sel局义、
- (id)forwardingTargetForSelector:(SEL)aSelector喜爷、
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector、
- (void)forwardInvocation:(NSInvocation *)anInvocation
我們首先封裝一個(gè)測(cè)試類(lèi)TestClass萄唇,其中需要包含遵守協(xié)議檩帐,并添加公有屬性、私有屬性另萤、私有成員變量湃密、公有實(shí)例方法、私有實(shí)例方法、類(lèi)方法等泛源。
這些內(nèi)容的添加主要便于之后的測(cè)試拔妥。
TestClass方法變量聲明.png
TestClass私有變量.png
TestClass方法實(shí)現(xiàn).png
一、動(dòng)態(tài)獲悉類(lèi)結(jié)構(gòu)
1. 動(dòng)態(tài)獲取類(lèi)名
采用class_getName(cls)在運(yùn)行時(shí)獲取類(lèi)的名稱(chēng)俩由。將char類(lèi)型的指針轉(zhuǎn)換成NSString類(lèi)型進(jìn)行返回毒嫡。
獲取類(lèi)名.png
2. 動(dòng)態(tài)獲取成員變量
采用class_copyIvarList(cls, count)獲取成員變量列表。使用ivar_getName(variable)來(lái)輸出成員變量名稱(chēng)幻梯,ivar_getTypeEncoding(variable)來(lái)輸出成員變量類(lèi)型兜畸。
我們通過(guò)將所得數(shù)據(jù)組合成NSDictionary來(lái)存儲(chǔ)單個(gè)變量,若干個(gè)字典組成NSArray作為屬性列表的返回碘梢。
獲取成員變量.png
使用TestClass進(jìn)行用例測(cè)試咬摇。由于是調(diào)用上述方法獲取TestClass的成員變量,到了運(yùn)行時(shí)階段實(shí)際就不存在公有私有之分煞躬。OC中的類(lèi)在ARC情況下添加的屬性肛鹏,其實(shí)就是自動(dòng)生成其get方法與set方法。
所有獲取的成員列表中肯定帶有成員屬性恩沛,成員屬性的名稱(chēng)前方帶有下劃線(xiàn)用于成員變量進(jìn)行區(qū)分在扰。
下方中各基本類(lèi)型由特殊字母代替,可以看出i代表int類(lèi)型雷客,c代表bool類(lèi)型芒珠,d表示double類(lèi)型,f表示float類(lèi)型搅裙。而如果是引用類(lèi)型則直接是一個(gè)字符串顯示皱卓,比如NSString類(lèi)型就是@"NSString"。
測(cè)試成員列表打印.png
3. 動(dòng)態(tài)獲取成員屬性列表
上方獲取了類(lèi)的成員變量部逮,那么下方進(jìn)行屬性列表的獲取娜汁。屬性區(qū)分于變量主要是它們擁有完整的set方法和get方法。
我們使用class_copyPropertyList(cls, count)來(lái)獲取屬性列表兄朋,通過(guò)property_getName(property)來(lái)獲取屬性名稱(chēng)掐禁。
獲取屬性列表.png
下方dynamic的屬性是我們使用runtime進(jìn)行動(dòng)態(tài)添加的。
測(cè)試屬性列表打印.png
4. 獲取類(lèi)的實(shí)例方法
我們通過(guò)class_copyMethodList(cls, count)來(lái)獲取實(shí)例方法列表蜈漓,通過(guò)method_getName(method)來(lái)獲取實(shí)例方法名稱(chēng)穆桂。
獲取實(shí)例方法.png
下方打印了所有TestClass類(lèi)的實(shí)例方法,當(dāng)然包括成員屬性的set方法和get方法融虽。其中.cxx_destruct方法不確認(rèn)歸屬于何處享完,也許dealloc方法的自我實(shí)現(xiàn)?
測(cè)試實(shí)例方法列表打印.png
5. 獲取類(lèi)的協(xié)議列表
我們使用class_copyProtocolList(cls, count)來(lái)獲取協(xié)議列表有额,使用protocol_getName(protocol)來(lái)獲取協(xié)議名稱(chēng)
獲取協(xié)議列表.png
二般又、動(dòng)態(tài)操作類(lèi)方法
1. 動(dòng)態(tài)添加方法實(shí)現(xiàn)
其添加原理旨在使用class_getInstanceMethod(cls, methodName)獲取相關(guān)的方法聲明以及使用method_getImplementation(method)獲取相關(guān)的方法實(shí)現(xiàn)彼绷。將它們進(jìn)行組合后,使用class_addMethod(cls, methodName, method, type)進(jìn)行方法的添加茴迁。
動(dòng)態(tài)添加方法實(shí)現(xiàn).png
2. 實(shí)現(xiàn)方法交換
通過(guò)class_getInstanceMethod(cls, methodName)獲取到需要交換的兩個(gè)方法寄悯,直接使用method_exchangeImplementation(methodA, methodB)進(jìn)行方法替換即可。
實(shí)現(xiàn)方法交換.png
通過(guò)類(lèi)目為測(cè)試類(lèi)封裝一個(gè)針對(duì)交換方法的測(cè)試用例堕义。
如果是普通情況下猜旬,沒(méi)有交換。在replaceMethod中調(diào)用本身勢(shì)必會(huì)造成死循環(huán)倦卖。
如是如果交換方法成功洒擦,那么此時(shí)在replaceMethod中調(diào)用replaceMethod,其實(shí)此時(shí)調(diào)用的是exchangeMethodA怕膛。由于exchangeMethodA不存在死循環(huán)熟嫩,故在測(cè)試時(shí),調(diào)用了封裝的交換方法后褐捻,進(jìn)一步又調(diào)用了replaceMethod掸茅,其實(shí)只是調(diào)用了exchangeMethodA而已。
交換方法的封裝.png
三柠逞、屬性關(guān)聯(lián)
屬性關(guān)聯(lián)可以說(shuō)是runtime最普通的打開(kāi)方式了昧狮。通過(guò)為屬性聲明一個(gè)靜態(tài)名稱(chēng),調(diào)用void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)實(shí)現(xiàn)新增屬性的set方法板壮,調(diào)用id objc_getAssociatedObject(id object, const void *key)實(shí)現(xiàn)新增屬性的get方法即可陵且。
動(dòng)態(tài)添加屬性.png
四、消息處理與消息轉(zhuǎn)發(fā)
消息處理過(guò)程:
當(dāng)你調(diào)用一個(gè)類(lèi)的方法時(shí)个束,先在本類(lèi)中的方法緩存列表中進(jìn)行查詢(xún),如果在緩存列表中找到了該方法的實(shí)現(xiàn)聊疲,就執(zhí)行茬底;如果找不到就在本類(lèi)中的方法列表中進(jìn)行查找。
在本類(lèi)方法列表中查找到相應(yīng)的方法實(shí)現(xiàn)后就進(jìn)行調(diào)用获洲,如果沒(méi)找到阱表,就去父類(lèi)中進(jìn)行查找;如果在父類(lèi)中的方法列表中找到了相應(yīng)方法的實(shí)現(xiàn)贡珊。
當(dāng)在方法緩存列表最爬,本類(lèi)中的方法列表以及父類(lèi)中的方法列表中都找不到相應(yīng)的實(shí)現(xiàn),到程序崩潰以前還會(huì)經(jīng)歷以下過(guò)程:
消息處理
如果一直尋找方法直到父類(lèi)中都找不到方法實(shí)現(xiàn)時(shí)會(huì)執(zhí)行+ (BOOL)resolveInstanceMethod:(SEL)sel類(lèi)方法门岔。
如果返回NO爱致,則表明不做任何處理,繼續(xù)下一步寒随。如果返回YES糠悯,就說(shuō)明該方法中對(duì)找不到實(shí)現(xiàn)的方法進(jìn)行了處理帮坚。
我們就可以在此方法中為找不到實(shí)現(xiàn)的SEL動(dòng)態(tài)添加一個(gè)方法實(shí)現(xiàn),添加完畢后互艾,就會(huì)執(zhí)行我們添加的方法實(shí)現(xiàn)试和。
下一次程序再找不到該類(lèi)某個(gè)方法的實(shí)現(xiàn)時(shí),就不會(huì)因?yàn)檎也坏蕉罎⒘恕?/p>
消息處理.png
2.消息轉(zhuǎn)發(fā)
如果不對(duì)上述消息進(jìn)行處理的話(huà)纫普,也就是+ (BOOL)resolveInstanceMethod:(SEL)sel方法返回NO時(shí)阅悍。便進(jìn)入了下一步消息轉(zhuǎn)發(fā)。
即執(zhí)行- (id)forwardingTargetForSelector:(SEL)aSelector方法昨稼。該方法會(huì)返回一個(gè)類(lèi)對(duì)象节视,該類(lèi)的對(duì)象有SEL對(duì)應(yīng)的實(shí)現(xiàn),當(dāng)調(diào)用這個(gè)找不到方法時(shí)悦昵,就會(huì)轉(zhuǎn)發(fā)到ExtClass中進(jìn)行處理肴茄。
此時(shí)完成消息轉(zhuǎn)發(fā)。如果該方法返回self或者nil但指,說(shuō)明不對(duì)相應(yīng)的方法進(jìn)行轉(zhuǎn)發(fā)寡痰,那就再走下一步。
消息轉(zhuǎn)發(fā).png
3.消息常規(guī)轉(zhuǎn)發(fā)
如果不將消息轉(zhuǎn)發(fā)給其他類(lèi)的對(duì)象棋凳,則此時(shí)代表自己進(jìn)行處理拦坠。即上述的方法中返回self或者nil。
此時(shí)執(zhí)行- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector來(lái)獲取方法的參數(shù)以及返回?cái)?shù)據(jù)類(lèi)型剩岳,即可以理解為該方法的簽名贞滨。
如果此時(shí)再次返回nil,那么消息轉(zhuǎn)發(fā)結(jié)束拍棕。程序崩潰晓铆,報(bào)出找不到相應(yīng)的方法實(shí)現(xiàn)的崩潰消息。
下方方法執(zhí)行的先決條件绰播,是要在+ (BOOL)resolveInstanceMethod:(SEL)sel中返回NO骄噪。然后下方也是進(jìn)行將方法轉(zhuǎn)給ExtClass的實(shí)現(xiàn)。
消息常規(guī)轉(zhuǎn)發(fā).png
本文項(xiàng)目Github鏈接地址:https://github.com/LibertyLeo/Runtime-Usage