標籤:
在之前的文章中我們介紹了Runtime是什麼,屬於理論性介紹,你看了上篇很迫切的想知道Runtime到底能幹什嗎?不要著急,這一篇Blog將將講解Runtime怎麼應用到實戰中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([self class], &count);
- for (int i = 0; i < count ; i++)
- {
- const char* propertyName = ivar_getName(ivars[i]);
- NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
- if ([attName isEqualToString:strName]) {
- flag = YES;
- }
- NSLog(@"===%@",strName);
- }
- return flag;
- }
沒錯,這個函數就是能幫你檢查是否有某個屬性或變數,下面講解下一個代碼:*
Ivar 原型是
typedef struct objc_ivar *Ivar; *
class_copyIvarList 返回的是某個類所有屬性或變數原型
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) ; *
ivar_getName 返回的是沒有 Ivar 結構體的名字,即變數的名字 原型
const char *ivar_getName(Ivar v); * 與這個對應的還有一個函數
class_copyPropertyList與
class_copyIvarList 不同點在前者只取屬性(
@property申明的屬性) 後者所有的 包括在interface大括弧中申明的。 class_copyPropertyList使用的範例程式碼如下:
- objc_property_t* properties= class_copyPropertyList([self class], &count);
- for (int i = 0; i < count ; i++)
- {
- const char* propertyName = property_getName(properties[i]);
- NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
- NSLog(@"===%@",strName);
- }
兩個不同可以用代碼來示範的,具體代碼自己動手寫 我就不貼出來,看看兩者到底有什麼區別? 有了這一步 你還擔心濫用KVC時崩潰了嗎?
動態建立函數有時候會根據項目需求動態建立某個函數,沒錯Runtime完全能做到 先看代碼:
- void dynamicMethod(id self, SEL _cmd)
- {
- printf("SEL %s did not exist\n",sel_getName(_cmd));
- }
-
- + (BOOL) resolveInstanceMethod:(SEL)aSEL
- {
-
- class_addMethod([self class], aSEL, (IMP)dynamicMethod, "[email protected]:");
- return YES;
- }
- void dynamicMethod(id self, SEL _cmd)
- {
- printf("SEL %s did not exist\n",sel_getName(_cmd));
- }
-
- + (BOOL) resolveInstanceMethod:(SEL)aSEL
- {
- class_addMethod([self class], aSEL, (IMP)dynamicMethod, "[email protected]:");
- return YES;
- }
測試代碼:
- RuntimeObj *obj = [[RuntimeObj alloc]init];
- [obj performSelector:@selector(dynamicMethod:)];
看看代碼運行效果 講解:
* + (BOOL) resolveInstanceMethod:(SEL)aSEL 是在調用此類方法時,如果沒有這個方法就會掉這個函數。
* class_addMethod 就是動態給類添加方法 原型
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
* “
[email protected]:” 是參數的一種寫法 以後會做詳細講解
替換已有函數在混合編碼的時候不能按照已有思路執行原來的函數,那我們把它替換掉不就好了嘛,看Runtime是怎麼做到的? 先上代碼:(注講下面的代碼是為了講targetReplaceMethod 替換成 demoReplaceMethod)
- void demoReplaceMethod(id SELF, SEL _cmd)
- {
- NSLog(@"demoReplaceMethod");
- }
-
- -(void)replaceMethod
- {
- Class strcls = [self class];
- SEL targetSelector = @selector(targetRelplacMethod);
- class_replaceMethod(strcls,targetSelector,(IMP)demoReplaceMethod,NULL);
- }
-
- -(void)targetRelplacMethod
- {
- NSLog(@"targetRelplacMethod");
- }
測試代碼:
- RuntimeObj *obj = [[RuntimeObj alloc]init];
- [obj replaceMethod];
- [obj targetRelplacMethod];
運行結果:
- 2014-05-12 19:38:37.490 Runtime[1497:303] demoReplaceMethod
是不是原來的
NSLog(@”targetRelplacMethod”); 這句話就沒有執行 被替換掉了! 註:1.
class_replaceMethod 方法就是動態替換
Method的函數,原型
IMP 。2.
class_replaceMethod(Class cls, SEL name,IMP imp, const char *types) 傳回值就是一個新函數的地址(
IMP指標)。3. 在實際項目中會經常用到這種方式, 比如:iOS 7以及7以下繪製NavigationBar, 自己慢慢體會吧。
動態掛載對象掛載這個詞語大家應該並不陌生吧,但是在這裡有一點點微妙的不同,在這裡博主也不是很好解釋這個詞語到底什麼含義,那我來舉個例子吧 如:如果你在對象傳遞(傳參)的時候需要用到某個屬性,按照以往的思路:我繼承這個類重新一個新類就完事了,OK,這個思路沒有問題,但是你不覺得要建立一個.h和一個.m檔案有點麻煩?程式員都是懶惰的,要是有一個方法能直接講我想要的屬性掛載上前去豈不是更好?代碼簡單、易懂。看了標題你就應該知道Runtime能幫你實現你的願望。 下面就來講解下如何使用Runtime來 在已有對象上動態掛載另外一個對象。 先不說 直接放代碼(這裡以UIAlertView為例子):
- //掛載對象所需要的參數(UIAlertView掛載對象)
- static const char kRepresentedObject;
- -(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; 這個只是一個標記,但是必不可少 具體什麼作用沒做過調研,我覺得應該就是你掛載的一個標記
Runtime 應該會根據這個標記來區別被掛載對象是掛載在哪個執行個體上。2.
objc_setAssociatedObject 動態設定關聯對象(也就是掛載)。3.
objc_getAssociatedObject 動態擷取關聯對象 看到沒有這裡也要傳
kRepresentedObject 這個標記,好像有點證明我前面的猜想了。
Objective-C Runtime能做什嗎?