runtime的作用及实例

作者: 嗯o哼 | 来源:发表于2015-09-29 03:59 被阅读1148次

    什么是runtime

    runtime是底层的纯C语言的API,它包含的很多底层的语法。
    我们平时编写的OC代码,在程序运行过程中,最终都是转化成了runtime的C语言代码,
    runtime也称为运行时,它是OC的幕后工作者。
    

    runtime的作用

    runtime主要就是做一些底层的操作,如:
       1. 动态的添加对象的成员变量和方法
       2.动态交换两个方法的实现(可以替换系统的方法)
       3.获得某个类的所有成员方法、所有成员变量
       4. 实现分类也可以添加属性
       5.实现NSCoding的自动归档和解档
       6.实现字典转模型的自动转换
    

    替换系统方法,可以通过拦截系统的方法探究底层,比如block 的实现原理

    常用方法

    1.获取类中的方法

    Method class_getClassMethod(Method cls , SEL name)
    

    如:

    Method m = class_getClassMethod([Person class],@selector(setName:));
    

    2.获取对象中的方法

    Method class_getInstanceMethod(Method cls, SEL name)
    

    如:

    Person *person = [[Person alloc] init];
    Method m = get_InstanceMethod([person class],@selector(setName:));
    

    3.交换两个方法的实现

     void method_exchangeImplementations(Method m1,Method m2)
    

         Person *p =[[Person alloc] init];
        [p study];
        [p run];
        //交换实现
        //instance method :实例方法,
        //class_getInstanceMethod得到实例的方法(即对象方法)
        //两个参数 1:类名 2.方法名
        //class_getClassMethod :得到实例化的方法
        Method m1 = class_getInstanceMethod([Person class], @selector(study));
        Method m2 = class_getInstanceMethod([Person class], @selector(run));
        method_exchangeImplementations(m2, m1);
        [p study];
        [p run];
    

    具体操作


    051B23EE-AFC6-4C95-9297-1E58708D5B96.png

    4.获取成员变量

    Ivar  *ivars = class_getCopyIvarList(Ivar ivar);
    

    实现分类中添加属性

    为所有的NSObject对象添加属性
    1.首先创建一个NSObject分类NSObject+Extension
    2.在.h中使用@property添加属性

    此时使用@property添加数属性,并非真正的属性,如果此时调用查看属性,将会崩溃,
    因为分类并未实现添加添加属性的功能,想要添加属性,需要使用runtime,动态的添加
    

    3.在.m文件中实现getter和setter方法

    如果想要添加多个属性,就需要在每个对象中抽出一块空间用于存放属性,
    使用objc_setAssociatedObject方法进行关联
    
    #import "NSObject+Extension.h"
    #import <objc/runtime.h>
    @implementation NSObject (Extension)
    //用于存放属性的变量,多个属性,需要创建不同的变量
    char BookKey;
    -(void)setBooks:(NSArray *)books{
    objc_setAssociatedObject(self, &BookKey, books, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    -(NSArray *)books{
    return objc_getAssociatedObject(self, &BookKey);
    }
    @end
    

    遵守协议NSCoding,实现属性的自动归档与解档

    需求分析:
    当想要对象自动进行归档解档的时候,如果属性非常的多,一个一个天添加[encoder encodeObject:@(xxx) forKey:@"_xxx"];将会非常的繁琐。
    既然能够获取所有的属性,我们就可以通过循环遍历属性的方式进行统一的归档和解档

    - (id)initWithCoder:(NSCoder *)decoder
    {
    if (self = [super init]) {
        // 用来存储成员变量的数量
        unsigned int outCount = 0;
        
        // 获得Dog类的所有成员变量
        Ivar *ivars = class_copyIvarList([self class], &outCount);
        
        // 遍历所有的成员变量
        for (int i = 0; i<outCount; i++) {
            // 取出i位置对应的成员变量
            Ivar ivar = ivars[i];
            
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            // 获得key对应的值
            id value = [decoder decodeObjectForKey:key];
            
            // 设置到成员变量上
            [self setValue:value forKeyPath:key];
        }
        
        free(ivars);
    }
    return self;
    }
    
    /**
     * 将对象写入文件时会调用这个方法(开发者需要在这个方法中说明需要存储哪些属性)
     */
    - (void)encodeWithCoder:(NSCoder *)encoder
    {
    // 用来存储成员变量的数量
    unsigned int outCount = 0;
    
    // 获得Dog类的所有成员变量
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    
    // 遍历所有的成员变量
    for (int i = 0; i<outCount; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivars[i];
        
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 通过key获得对应成员变量的值
        id value = [self valueForKeyPath:key];
        
        [encoder encodeObject:value forKey:key];
    }
    
    free(ivars);
    }
    

    注意:

    ARC的内存管理机制 只适合OC语法,对于C语言的内存还是需要手动的释放,当使用runtime的时候,
    如果包含了copy、create、retain、new等词语,那么在最后就需要释放内存
    
    使用free(对象)进行释放如:free(ivars);
    

    利用runtime实现字典转模型

    描述:

    KVC的字典转模型具有一个缺陷,就是属性的数量与名称都必须保持一致,如果字典中的属性多,
    而模型中没有使用KVC赋值的时候就会崩溃,需要实现另一个方法 
    setValue:forUndefinedKey:方法,并如果对象中包含了另一个对象作为属性,
    也将不能自动将其转化为模型
    
    而使用runtime实现的字典转模型,可以实现将所有的对象都转化为对应的模型,
    并且不会出现属性找不到,而奔溃的现象
    

    NSObject+Extension.h
    #import <Foundation/Foundation.h>
    @interface NSObject (Extension)
    -(void)setDiction:(NSDictionary *)dict;
    +(instancetype)objectWithDiction:(NSDictionary *)dict;
    @end

    NSObject+Extension.m

    #import "NSObject+Extension.h"
    #import <objc/runtime.h>
    
    @implementation NSObject (Extension)
    
    -(void)setDiction:(NSDictionary *)dict{
    //获取类
    Class c = self.class;
    //循环遍历 类 (本类 和所有的父类)
    while (c && c != [NSObject class]) {
        //获取所有的属性
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        //遍历类中的属性
        for (int i = 0; i < outCount; i++) {
            //获取属性名
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            //去掉key中的 _
            key = [key substringFromIndex:1];
            //通过key 获取属性的值
            id value = dict[key];
            //如果key是一个空值 退出本轮的循环
            //原因:如果字典中没有这个key,那么value将会是一个空值,kvc 不能赋值空值
            if (value == nil) {
                continue;
            }
            //如果类中包含另一个类为对象,也要将该对象进行字典转模型
            //获取对象的属性的类名
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            // 对象名会以@“名字”的形式 出现,但是同时字符串也是以这种形式表示,因此可以先判断type中是否包含 @ 符号
            NSRange range = [type rangeOfString:@"@"];
            //如果range.location 不等于NSNotFound说明 找到了@
            if (range.location != NSNotFound) {
                //截取type中的名字 去除@“ ”
                type = [type substringWithRange:NSMakeRange(2, type.length -3)];
                if (![type hasPrefix:@"NS"]) {
                    //将type转化为类名
                    Class class = NSClassFromString(type);
                    value = [class objectWithDiction:value];
                }
                
            }
            
            //赋值
            [self setValue:value forKey:key];
            
        }
        //ARC 只适用于OC语法,C语言中的内存 需要手动释放
        free(ivars);
        c = [c superclass];
        NSLog(@"1");
      }
    }
    +(instancetype)objectWithDiction:(NSDictionary *)dict{
    NSObject *obj = [[self alloc] init];
    [obj setDiction:dict];
    return  obj;
    }  
    @end

    相关文章

      网友评论

      本文标题:runtime的作用及实例

      本文链接:https://www.haomeiwen.com/subject/kpiwcttx.html