Objective-C Runtime能做什么?

来源:互联网 发布:centos 7 配置xshell 编辑:程序博客网 时间:2024/04/30 07:30

http://www.anselz.com/?p=214

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. {
  3. BOOL flag = NO;
  4. u_int count;
  5. Ivar *ivars = class_copyIvarList([self class], &count);
  6. for (int i = 0; i < count ; i++)
  7. {
  8. const char* propertyName = ivar_getName(ivars[i]);
  9. NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
  10. if ([attName isEqualToString:strName]) {
  11. flag = YES;
  12. }
  13. NSLog(@"===%@",strName);
  14. }
  15. return flag;
  16. }

没错,这个函数就是能帮你检查是否有某个属性或变量,下面讲解下一个代码

  • 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. {
  4. const char* propertyName = property_getName(properties[i]);
  5. NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
  6. NSLog(@"===%@",strName);
  7. }

两个不同可以用代码来演示的,具体代码自己动手写 我就不贴出来,看看两者到底有什么区别?

有了这一步 你还担心滥用KVC时崩溃了么?

 动态创建函数

 

有时候会根据项目需求动态创建某个函数,没错Runtime完全能做到

先看代码:

  1. void dynamicMethod(id self, SEL _cmd)
  2. {
  3. printf("SEL %s did not exist\n",sel_getName(_cmd));
  4. }
  5.  
  6. + (BOOL) resolveInstanceMethod:(SEL)aSEL
  7. {
  8. class_addMethod([self class], aSEL, (IMP)dynamicMethod, "v@:");
  9. return YES;
  10. }
  11. void dynamicMethod(id self, SEL _cmd)
  12. {
  13. printf("SEL %s did not exist\n",sel_getName(_cmd));
  14. }
  15.  
  16. + (BOOL) resolveInstanceMethod:(SEL)aSEL
  17. {
  18. class_addMethod([self class], aSEL, (IMP)dynamicMethod, "v@:");
  19. return YES;
  20. }

测试代码:

  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. {
  3. NSLog(@"demoReplaceMethod");
  4. }
  5.  
  6. -(void)replaceMethod
  7. {
  8. Class strcls = [self class];
  9. SEL targetSelector = @selector(targetRelplacMethod);
  10. class_replaceMethod(strcls,targetSelector,(IMP)demoReplaceMethod,NULL);
  11. }
  12.  
  13. -(void)targetRelplacMethod
  14. {
  15. NSLog(@"targetRelplacMethod");
  16. }

测试代码:

  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”); 这句话就没有执行 被替换掉了 哈哈~

注:

  • class_replaceMethod 方法就是动态替换Method的函数,原型 IMP class_replaceMethod(Class cls, SEL name,IMP imp, const char *types) 返回值就是一个新函数的地址(IMP指针
  • 在实际项目中会经常用到这种方式 比如:iOS 7以及7以下绘制NavigationBar 自己慢慢体会吧

动态挂载对象

 

挂载这个词语大家应该并不陌生吧,但是在这里有一点点微妙的不同,在这里博主也不是很好解释这个词语到底什么含义,那我来举个例子吧

如:如果你在对象传递(传参)的时候需要用到某个属性,按照以往的思路:我继承这个类重新一个新类就完事了,OK,这个思路没有问题,但是你不觉得要新建一个.h和一个.m文件有点麻烦?程序员都是懒惰的,要是有一个方法能直接讲我想要的属性挂载上前去岂不是更好?代码简单、易懂。看了标题你就应该知道Runtime能帮你实现你的愿望。

下面就来讲解下如何使用Runtime来 在已有对象上动态挂载另外一个对象。

先不说 直接放代码(这里以UIAlertView为例子):

  1. //挂载对象所需要的参数(UIAlertView挂载对象)
  2. static const char kRepresentedObject;
  3. -(void)showAlert:(id)sender
  4. {
  5. UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"去看看", nil];
  6. alert.tag = ALERT_GOTO_TAG;
  7. objc_setAssociatedObject(alert, &kRepresentedObject,
  8. @"我是被挂载的",
  9. OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  10. [alert show];
  11. }

这个只是挂载看看如何去获取我们挂载的对象(NSString @“我是被挂载的”)

  1. -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
  2. {
  3. if (buttonIndex == 1) {
  4. NSString *str = objc_getAssociatedObject(alertView,
  5. &kRepresentedObject);
  6. NSLog(@"%@",str)
  7. }
  8. }

自己动手编写代码看看效果 是不是和你想的一样?

下面讲解下:

  • static const char kRepresentedObject; 这个只是一个标记,但是必不可少 具体什么作用没做过调研,我觉得应该就是你挂载的一个标记 Runtime应该会根据这个标记来区别被挂载对象是挂载在哪个实例上
  • objc_setAssociatedObject 动态设置关联对象(也就是挂载)
  • objc_getAssociatedObject 动态获取关联对象 看到没有这里也要传 kRepresentedObject 这个标记,好像有点证明我前面的猜想了

 

总结:

博主暂时就准备了这么点Runtime得实战应用,如果还有以后再补充

更多接口还是见官方文档


0 0