• Objective-C Runtime能做什么?


    之前的文章中我们介绍了Runtime是什么,属于理论性介绍,你看了上篇很迫切的想知道Runtime到底能干什么?不要着急,这一篇Blog将将讲解Runtime怎么应用到实战中Runtime官方文档在这里,包括了接口名字以及使用说明。下文讲到的接口都能在此文档中找到。

     
    KVC中setValue中使用
    我们知道在KVC中如果直接setValue如果对象没有这个属性或者是变量就会直接Crash,如:
    1. RuntimeObj *obj = [[RuntimeObj alloc]init]; 
    2. [obj setValue:@"value4Name" forKey:@"objName"];//RuntimeObj 没有objName这个属性 
    这段代码会直接Crash
     
    有没有对这个感觉头疼,要是能先用某种方式检查下再set那就不会Crash? 没错,这件事情Runtime能做到
     
    先看一下示例代码吧:
    1. -(BOOL)hasAttribute:(NSString *)attName 
    2.     BOOL flag = NO; 
    3.     u_int               count; 
    4.     Ivar *ivars = class_copyIvarList([self class], &count); 
    5.     for (int i = 0; i < count ; i++) 
    6.     { 
    7.         const char* propertyName = ivar_getName(ivars[i]); 
    8.         NSString *strName = [NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]; 
    9.         if ([attName isEqualToString:strName]) { 
    10.             flag = YES; 
    11.         } 
    12.         NSLog(@"===%@",strName); 
    13.     } 
    14.     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_copyPropertyListclass_copyIvarList 不同点在前者只取属性(@property申明的属性) 后者所有的 包括在interface大括号中申明的。
     
    class_copyPropertyList使用的示例代码如下:
    1. objc_property_t*    properties= class_copyPropertyList([self class], &count); 
    2. for (int i = 0; i < count ; i++) 
    3.      const char* propertyName = property_getName(properties[i]); 
    4.      NSString *strName = [NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]; 
    5.       NSLog(@"===%@",strName); 
      
    两个不同可以用代码来演示的,具体代码自己动手写 我就不贴出来,看看两者到底有什么区别?
     
    有了这一步 你还担心滥用KVC时崩溃了么?
     
    动态创建函数
    有时候会根据项目需求动态创建某个函数,没错Runtime完全能做到
     
    先看代码:
    1. void dynamicMethod(id self, SEL _cmd) 
    2.     printf("SEL %s did not exist ",sel_getName(_cmd)); 
    3.   
    4. + (BOOL) resolveInstanceMethod:(SEL)aSEL 
    5.     
    6.     class_addMethod([self class], aSEL, (IMP)dynamicMethod, "v@:"); 
    7.     return YES; 
    8. void dynamicMethod(id self, SEL _cmd) 
    9.     printf("SEL %s did not exist ",sel_getName(_cmd)); 
    10.   
    11. + (BOOL) resolveInstanceMethod:(SEL)aSEL 
    12.     class_addMethod([self class], aSEL, (IMP)dynamicMethod, "v@:"); 
    13.     return YES; 
     
    测试代码:
    1. RuntimeObj *obj = [[RuntimeObj alloc]init]; 
    2. [obj performSelector:@selector(dynamicMethod:)]; 
     
    看看代码运行效果
     
    讲解: 
    * + (BOOL) resolveInstanceMethod:(SEL)aSEL 是在调用此类方法时,如果没有这个方法就会掉这个函数。
     
    * class_addMethod 就是动态给类添加方法 原型 BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)  
     
     注:IMP 是函数指针
     
    * “v@:” 是参数的一种写法 以后会做详细讲解
      
    替换已有函数
    在混合编码的时候不能按照已有思路执行原来的函数,那我们把它替换掉不就好了嘛,看Runtime是怎么做到的?
     
    先上代码:(注讲下面的代码是为了讲targetReplaceMethod 替换成 demoReplaceMethod)
    1. void demoReplaceMethod(id SELF, SEL _cmd) 
    2.     NSLog(@"demoReplaceMethod"); 
    3.   
    4. -(void)replaceMethod 
    5.     Class strcls = [self class]; 
    6.     SEL  targetSelector = @selector(targetRelplacMethod); 
    7.     class_replaceMethod(strcls,targetSelector,(IMP)demoReplaceMethod,NULL); 
    8.   
    9. -(void)targetRelplacMethod 
    10.     NSLog(@"targetRelplacMethod"); 
     
    测试代码:
    1. RuntimeObj *obj = [[RuntimeObj alloc]init]; 
    2. [obj replaceMethod]; 
    3. [obj targetRelplacMethod]; 
     
    运行结果:
    1. 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为例子):
    1. //挂载对象所需要的参数(UIAlertView挂载对象) 
    2. static const char kRepresentedObject; 
    3. -(void)showAlert:(id)sender 
    4.     UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"去看看", nil]; 
    5.     alert.tag = ALERT_GOTO_TAG; 
    6.     objc_setAssociatedObject(alert, &kRepresentedObject, 
    7.     @"我是被挂载的", 
    8.     OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
    9.     [alert show]; 
     
    这个只是挂载看看如何去获取我们挂载的对象(NSString @“我是被挂载的”)
    1. -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 
    2.     if (buttonIndex == 1) { 
    3.         NSString *str = objc_getAssociatedObject(alertView, 
    4.                                                     &kRepresentedObject); 
    5.         NSLog(@"%@",str) 
    6.     }  
     
    自己动手编写代码看看效果 是不是和你想的一样?
     
    下面讲解下:
    1. static const char kRepresentedObject; 这个只是一个标记,但是必不可少 具体什么作用没做过调研,我觉得应该就是你挂载的一个标记 Runtime 应该会根据这个标记来区别被挂载对象是挂载在哪个实例上。
    2. objc_setAssociatedObject 动态设置关联对象(也就是挂载)。
    3. objc_getAssociatedObject 动态获取关联对象 看到没有这里也要传 kRepresentedObject 这个标记,好像有点证明我前面的猜想了。
  • 相关阅读:
    Delphi 与 DirectX 之 DelphiX(60): TDIB.DoTrace();
    Delphi 与 DirectX 之 DelphiX(62): TDIB.DoSolorize();
    Delphi 与 DirectX 之 DelphiX(55): TDIB.DoMosaic();
    利用ImportFromRSS可以方便地收藏其他网站的文章
    粘贴代码更简单的方法(保持VS.NET中的格式)
    请正确填写你的邮件地址
    介绍.Text 中的TrackBack
    在VS.NET中给xsl文件增加智能感知功能
    The Difference Between RegisterStartupScript and RegisterClientScriptBlock
    [转帖]Why you can't catch some exceptions
  • 原文地址:https://www.cnblogs.com/iOSJason/p/5548197.html
Copyright © 2020-2023  润新知