在之前的文章中我們介紹了Runtime是什么跪解,屬于理論性介紹炉旷,你看了上篇很迫切的想知道Runtime到底能干什么?不要著急,這一篇Blog將將講解Runtime怎么應(yīng)用到實戰(zhàn)中Runtime官方文檔在這里窘行,包括了接口名字以及使用說明饥追。下文講到的接口都能在此文檔中找到。
KVC中setValue中使用
我們知道在KVC中如果直接setValue如果對象沒有這個屬性或者是變量就會直接Crash罐盔,如:
RuntimeObj?*obj?=?[[RuntimeObj?alloc]init];
[obj?setValue:@"value4Name"forKey:@"objName"];//RuntimeObj?沒有objName這個屬性
這段代碼會直接Crash
有沒有對這個感覺頭疼但绕,要是能先用某種方式檢查下再set那就不會Crash? 沒錯惶看,這件事情Runtime能做到
先看一下示例代碼吧:
-(BOOL)hasAttribute:(NSString?*)attName
{
BOOL?flag?=?NO;
u_int???????????????count;
Ivar?*ivars?=?class_copyIvarList([selfclass],?&count);
for(inti?=?0;?i?<?count?;?i++)
{
constchar*?propertyName?=?ivar_getName(ivars[i]);
NSString?*strName?=?[NSString??stringWithCString:propertyName?encoding:NSUTF8StringEncoding];
if([attName?isEqualToString:strName])?{
flag?=?YES;
}
NSLog(@"===%@",strName);
}
returnflag;
}
沒錯捏顺,這個函數(shù)就是能幫你檢查是否有某個屬性或變量,下面講解下一個代碼:
*Ivar原型是typedef struct objc_ivar *Ivar;
*class_copyIvarList返回的是某個類所有屬性或變量原型Ivar *class_copyIvarList(Class cls, unsigned int *outCount)纬黎;
*ivar_getName返回的是沒有 Ivar 結(jié)構(gòu)體的名字幅骄,即變量的名字 原型const char *ivar_getName(Ivar v);
* 與這個對應(yīng)的還有一個函數(shù)class_copyPropertyList與class_copyIvarList不同點在前者只取屬性(@property申明的屬性) 后者所有的 包括在interface大括號中申明的莹桅。
class_copyPropertyList使用的示例代碼如下:
objc_property_t*????properties=?class_copyPropertyList([selfclass],?&count);
for(inti?=?0;?i?<?count?;?i++)
{
constchar*?propertyName?=?property_getName(properties[i]);
NSString?*strName?=?[NSString??stringWithCString:propertyName?encoding:NSUTF8StringEncoding];
NSLog(@"===%@",strName);
}
兩個不同可以用代碼來演示的昌执,具體代碼自己動手寫 我就不貼出來,看看兩者到底有什么區(qū)別诈泼?
有了這一步 你還擔(dān)心濫用KVC時崩潰了么懂拾?
動態(tài)創(chuàng)建函數(shù)
有時候會根據(jù)項目需求動態(tài)創(chuàng)建某個函數(shù),沒錯Runtime完全能做到
先看代碼:
voiddynamicMethod(id?self,?SEL?_cmd)
{
printf("SEL?%s?did?not?exist\n",sel_getName(_cmd));
}
+?(BOOL)?resolveInstanceMethod:(SEL)aSEL
{
class_addMethod([selfclass],?aSEL,?(IMP)dynamicMethod,"v@:");
returnYES;
}
voiddynamicMethod(id?self,?SEL?_cmd)
{
printf("SEL?%s?did?not?exist\n",sel_getName(_cmd));
}
+?(BOOL)?resolveInstanceMethod:(SEL)aSEL
{
class_addMethod([selfclass],?aSEL,?(IMP)dynamicMethod,"v@:");
returnYES;
}
測試代碼:
RuntimeObj?*obj?=?[[RuntimeObj?alloc]init];
[obj?performSelector:@selector(dynamicMethod:)];
看看代碼運行效果
講解:
* + (BOOL) resolveInstanceMethod:(SEL)aSEL是在調(diào)用此類方法時铐达,如果沒有這個方法就會掉這個函數(shù)岖赋。
* class_addMethod就是動態(tài)給類添加方法 原型BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
注:IMP 是函數(shù)指針
* “v@:” 是參數(shù)的一種寫法 以后會做詳細講解
替換已有函數(shù)
在混合編碼的時候不能按照已有思路執(zhí)行原來的函數(shù),那我們把它替換掉不就好了嘛瓮孙,看Runtime是怎么做到的唐断?
先上代碼:(注講下面的代碼是為了講targetReplaceMethod 替換成 demoReplaceMethod)
voiddemoReplaceMethod(id?SELF,?SEL?_cmd)
{
NSLog(@"demoReplaceMethod");
}
-(void)replaceMethod
{
Class?strcls?=?[selfclass];
SEL??targetSelector?=?@selector(targetRelplacMethod);
class_replaceMethod(strcls,targetSelector,(IMP)demoReplaceMethod,NULL);
}
-(void)targetRelplacMethod
{
NSLog(@"targetRelplacMethod");
}
測試代碼:
RuntimeObj?*obj?=?[[RuntimeObj?alloc]init];
[obj?replaceMethod];
[obj?targetRelplacMethod];
運行結(jié)果:
2014-05-12?19:38:37.490?Runtime[1497:303]?demoReplaceMethod
是不是原來的NSLog(@”targetRelplacMethod”);這句話就沒有執(zhí)行 被替換掉了!
注:
1.class_replaceMethod方法就是動態(tài)替換Method的函數(shù),原型IMP杭抠。
2.class_replaceMethod(Class cls, SEL name,IMP imp, const char *types)返回值就是一個新函數(shù)的地址(IMP指針)脸甘。
3. 在實際項目中會經(jīng)常用到這種方式, 比如:iOS 7以及7以下繪制NavigationBar偏灿, 自己慢慢體會吧丹诀。
動態(tài)掛載對象
掛載這個詞語大家應(yīng)該并不陌生吧,但是在這里有一點點微妙的不同翁垂,在這里博主也不是很好解釋這個詞語到底什么含義铆遭,那我來舉個例子吧
如:如果你在對象傳遞(傳參)的時候需要用到某個屬性,按照以往的思路:我繼承這個類重新一個新類就完事了沿猜,OK枚荣,這個思路沒有問題,但是你不覺得要新建一個.h和一個.m文件有點麻煩啼肩?程序員都是懶惰的橄妆,要是有一個方法能直接講我想要的屬性掛載上前去豈不是更好衙伶?代碼簡單、易懂呼畸『壑В看了標(biāo)題你就應(yīng)該知道Runtime能幫你實現(xiàn)你的愿望。
下面就來講解下如何使用Runtime來 在已有對象上動態(tài)掛載另外一個對象蛮原。
先不說 直接放代碼(這里以UIAlertView為例子):
//掛載對象所需要的參數(shù)(UIAlertView掛載對象)
staticconstcharkRepresentedObject;
-(void)showAlert:(id)sender
{
UIAlertView?*alert?=?[[UIAlertView?alloc]initWithTitle:@"提示"message:message?delegate:self?cancelButtonTitle:@"取消"otherButtonTitles:@"去看看",?nil];
alert.tag?=?ALERT_GOTO_TAG;
objc_setAssociatedObject(alert,?&kRepresentedObject,
@"我是被掛載的",
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[alert?show];
}
這個只是掛載看看如何去獲取我們掛載的對象(NSString @“我是被掛載的”)
-(void)alertView:(UIAlertView?*)alertView?clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex?==?1)?{
NSString?*str?=?objc_getAssociatedObject(alertView,
&kRepresentedObject);
NSLog(@"%@",str)
}
}
自己動手編寫代碼看看效果 是不是和你想的一樣卧须?
下面講解下:
1.static const char kRepresentedObject;這個只是一個標(biāo)記,但是必不可少 具體什么作用沒做過調(diào)研儒陨,我覺得應(yīng)該就是你掛載的一個標(biāo)記Runtime應(yīng)該會根據(jù)這個標(biāo)記來區(qū)別被掛載對象是掛載在哪個實例上花嘶。
2.objc_setAssociatedObject動態(tài)設(shè)置關(guān)聯(lián)對象(也就是掛載)。
3.objc_getAssociatedObject動態(tài)獲取關(guān)聯(lián)對象 看到?jīng)]有這里也要傳kRepresentedObject這個標(biāo)記蹦漠,好像有點證明我前面的猜想了椭员。
更多接口見官方文檔