zoukankan      html  css  js  c++  java
  • Objective-C 中基于RunTime实现的反射

    一、反射

    反射,一般表现在字符串和Class转换,字符串和内部方法转换,字符串和属性的转换(取值和赋值)。

    二、Objective-C中的反射

    OC的反射是基于其Runtime实现的。

    以执行某个函数为例,我们知道在OC中执行[Stu doSomething]函数,实质上是发送了一个消息给Runtime,然后Runtime再根据这个Class的字符串名和这个函数的字符串名,去匹配真正相应的方法的地址,然后再执行的。所以中间我们可以利用字符串去动态的检测,甚至动态的修改(之前说到的Method Swizzling)。

    在OC中,很多Runtime的动态特性的接口大致都已经在NSObject.h中声明,可以自己看下源码。 

    反射具体表现在:

    1.字符串和Class转换,及判断

    Class __nullable NSClassFromString(NSString *aClassName);
    -(BOOL)isKindOfClass:(Class)aClass;
    -(BOOL)isMemberOfClass:(Class)aClass;

      示例:

    //通过这样的方式获取class
    Class name = NSClassFromString(@"Student");  
    Student *stu = [[name alloc] init]; 
    
    //判断是否为其子类的对象 Student
    *stu = [[Student alloc]init]; if([stu isKindOfClass:[Person class]]){ NSLog(@"stu是Person类型或其子类"); }else{ NSLog(@"stu不是Person类型或其子类"); } //判断是否是该class的对象 if([stu isMemberOfClass:[Person class]]){ NSLog(@"stu是Person类型"); }else{ NSLog(@"stu不是Person类型"); }

    2.字符串和内部方法转换

    - (BOOL)respondsToSelector:(SEL)aSelector  判断类型或对象有没有某个方法
    + (BOOL)instancesRespondToSelector:(SEL)aSelector; //判断静态方法
    - (id)performSelector:(SEL)aSelector  动态调用对象的方法
    - (BOOL)conformsToProtocol:(Protocol *)aProtocol; 判断对象是否实现某个Protocol协议

    示例:

    //动态生成方法选择器
    SEL sel =  NSSelectorFromString(@"setAge:");  
    
    //检测是否存在某方法
    Student *stu = [[Student alloc]init];  
    if([stu respondsToSelector:@selector(setAge)]){  
        NSLog(@"stu 有setAge这个方法");  
    }else{  
        NSLog(@"没有");  
    }  
    
    //动态动用方法
    Student *stu = [[Student alloc]initAge:1];  
    int age = [stu performSelector:@selector(age)];  
    NSLog(@"%i",age);//输出1  
    
    //动态调用有参数的方法
    [stu  performSelector:@selector(setAge1:) withObject:@"2"];  

    3.字符串和属性的转换

    OC中属性的反射通过KVC(Key-Value Coding)机制实现,KVC是一种间接访问对象属性的机制,不直接调用getter 和 setter方法,而使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。

    之前的一篇博客(http://www.cnblogs.com/rayshen/p/5006619.html),在探讨如何把某个对象进行序列化的时候,其实已经使用到KVC,如果某个类遵循NSCoding协议就能编码成NSData(字节流)。

     

    具体KVC使用的示例为:

    Persion *persion =  [ [Persion alloc] init ];
    //不使用KVC
    persion.name = @"shen" ;
    //使用KVC的写法
    [persion  setValue:@"shen" forKey:@"name"];

    上面是利用KVC访问类里的某个属性,下面利用KVC直接访问类里的类里的某个属性

    //不使用KVC
    Persion *persion =  [ [Persion alloc] init ];
    Phone *phone = persion.phone;
    Battery *battery = phone.battery;
    
    //使用KVC
    Battery *battery = [persion valueForKeyPath: @"phone.battery" ];

    对于SetValueForKey,需要小心的是,假如类型匹配错误的情况下,编译会通过,但运行会报错(动态消息机制嘛,能理解)

    
    [persion setValue:[NSNumber numberWithInteger:1] forKey:@"name"]; 
    // 编译并运行,但报错
    
    persion.name = [NSNumber numberWithInteger:1]; 
    // 不能编译

    参考博客:

    http://blog.csdn.net/victormokai/article/details/19631359

    http://www.tuicool.com/articles/2aYfy2

    http://www.cnblogs.com/ygm900/archive/2013/01/16/2862676.html

    http://www.cnblogs.com/yjmyzz/archive/2011/02/28/1967451.html

    http://www.cnblogs.com/yaski/archive/2009/04/05/1429735.html

  • 相关阅读:
    java四种线程池的使用
    @Autowired@Resource@Qualifier的区别
    Unsupported major.minor version 52.0解决办法
    CentOS7配置防火墙
    redis 集群搭建
    excludepathpatterns 无效
    解决 SpringBoot 没有主清单属性
    Java Web应用中调优线程池的重要性
    spring boot application properties配置详解
    Class path contains multiple SLF4J bindings.
  • 原文地址:https://www.cnblogs.com/rayshen/p/5044618.html
Copyright © 2011-2022 走看看