• IOS Runtime(使用场景)


    概述

    Runtime的内容大概有:动态获取类名、动态获取类的成员变量、动态获取类的属性列表、动态获取类的方法列表、动态获取类所遵循的协议列表、动态添加新的方法、类的实例方法实现的交换、动态属性关联、消息发送与消息转发机制等。当然,本篇博客总结的是运行时常用的功能,并不是所有Runtime的内容。

    OC的方法调用流程

    1、编译器会把`[blackDog walk]`转化为`objc_msgSend(blackDog,SEL)`,SEL为@selector(walk)。
    
    2、Runtime会在blackDog对象所对应的Dog类的方法缓存列表里查找方法的SEL
    
    3、如果没有找到,则在Dog类的方法分发表查找方法的SEL。(类由对象isa指针指向,方法分发表即methodList)
    
    4、如果没有找到,则在其父类(设Dog类的父类为Animal类)的方法分发表里查找方法的SEL(父类由类的superClass指向)
    
    5、如果没有找到,则沿继承体系继续下去,最终到达NSObject类。
    
    6、如果在234的其中一步中找到,则定位了方法实现的入口,执行具体实现
    
    7、如果最后还是没有找到,会面临两种情况:``(1) 如果是使用`[blackDog walk]`的方式调用方法````(2) 使用`[blackDog performSelector:@selector(walk)]`的方式调用方法`
    1、动态方法解析
    接收到未知消息时(假设blackDog的walk方法尚未实现),runtime会调用+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方法)
    
    2、备用接收者
    如果以上方法没有做处理,runtime会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。
    如果该方法返回了一个非nil(也不能是self)的对象,而且该对象实现了这个方法,那么这个对象就成了消息的接收者,消息就被分发到该对象。
    适用情况:通常在对象内部使用,让内部的另外一个对象处理消息,在外面看起来就像是该对象处理了消息。
    比如:blackDog让女朋友whiteDog来接收这个消息
    
    3、完整消息转发
    在- (void)forwardInvocation:(NSInvocation *)anInvocation方法中选择转发消息的对象,其中anInvocation对象封装了未知消息的所有细节,并保留调用结果发送到原始调用者。
    比如:blackDog将消息完整转发給主人dogOwner来处理

    综上所述:

    1.静态消息查找:obj_msg(self,sel),在method list里面查找,在分类里面查找,在父类查找,一直查找

    2.动态消息查找:接收到未知消息时(假设blackDog的walk方法尚未实现),runtime会调用+resolveInstanceMethod:(实例方法)或者+resolveClassMethod:(类方法)

    3.备用接受着:如果以上方法没有做处理,runtime会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法。

     

    runtime的使用场景

    1.一键json->model

    //(1)获取类的属性及属性对应的类型
            NSMutableArray * keys = [NSMutableArray array];
            NSMutableArray * attributes = [NSMutableArray array];
            /*
             * 例子
             * name = value3 attribute = T@"NSString",C,N,V_value3
             * name = value4 attribute = T^i,N,V_value4
             */
            unsigned int outCount;
            objc_property_t * properties = class_copyPropertyList([self class], &outCount);
            for (int i = 0; i < outCount; i ++) {
                objc_property_t property = properties[i];
                //通过property_getName函数获得属性的名字
                NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
                [keys addObject:propertyName];
                //通过property_getAttributes函数可以获得属性的名字和@encode编码
                NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
                [attributes addObject:propertyAttribute];
            }
            //立即释放properties指向的内存
            free(properties);
    
            //(2)根据类型给属性赋值
            for (NSString * key in keys) {
                if ([dict valueForKey:key] == nil) continue;
                [self setValue:[dict valueForKey:key] forKey:key];
            }

    2.一键解档和归档

    - (id)initWithCoder:(NSCoder *)aDecoder {
        if (self = [super init]) {
            unsigned int outCount;
            Ivar * ivars = class_copyIvarList([self class], &outCount);
            for (int i = 0; i < outCount; i ++) {
                Ivar ivar = ivars[i];
                NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
                [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
            }
        }
        return self;
    }
    
    - (void)encodeWithCoder:(NSCoder *)aCoder {
        unsigned int outCount;
        Ivar * ivars = class_copyIvarList([self class], &outCount);
        for (int i = 0; i < outCount; i ++) {
            Ivar ivar = ivars[i];
            NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [aCoder encodeObject:[self valueForKey:key] forKey:key];
        }
    }

    访问私有变量

    Ivar ivar = class_getInstanceVariable([Model class], "_str1");
    NSString * str1 = object_getIvar(model, ivar);

    Method Swizzling

    method Swizzling原理
    每个类都维护一个方法(Method)列表,Method则包含SEL和其对应IMP的信息,方法交换做的事情就是把SEL和IMP的对应关系断开,并和新的IMP生成对应关系

    + (void)load {
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
    
            Class selfClass = object_getClass([self class]);
    
            SEL oriSEL = @selector(imageNamed:);
            Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);
    
            SEL cusSEL = @selector(myImageNamed:);
            Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);
    
            BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
            if (addSucc) {
                class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
            }else {
                method_exchangeImplementations(oriMethod, cusMethod);
            }
    
        });
    }

    NSAarray和NSDictionary的排错处理

    数据统计的统一处理

    runtime的功能

    1、获取类名

    动态的获取类名是比较简单的,使用class_getName(Class)就可以在运行时来获取类的名称。class_getName()函数返回的是一个char类型的指针,也就是C语言的字符串类型,所以我们要将其转换成NSString类型,然后再返回出去。下方的+fetchClassName:方法就是我们封装的获取类名的方法,如下所示:

    2、获取成员变量

    下方这个+fetchIvarList:这个方法就是我们封装的获取类的成员变量的方法。当然我们在获取成员变量时,可以用ivar_getTypeEncoding()来获取相应成员变量的类型。使用ivar_getName()来获取相应成员变量的名称。下方就是对获取成员变量的功能的封装。返回的是一个数组,数组的元素是一个字典,而字典中存储的就是相应成员变量的名称和类型。

    3.获取成员属性

    上面获取的是类的成员变量,那么下方这个+fetchPropertyList:获取的就是成员属性。当然此刻获取的只包括成员属性,也就是那些有setter或者getter方法的成员变量。下方主要是使用了class_copyPropertyList(Class,&count)来获取的属性列表,然后通过for循环通过property_getName()来获取每个属性的名字。当然使用property_getName()获取到的名字依然是C语言的char类型的指针,所以我们还需要将其转换成NSString类型,然后放到数组中一并返回。如下所示:

    4、获取类的实例方法

    接下来我们就来封装一下获取类的实例方法列表的功能,下方这个+fetchMethodList:就是我们封装的获取类的实例方法列表的函数。在下方函数中,通过class_copyMethodList()方法获取类的实例方法列表,然后通过for循环使用method_getName()来获取每个方法的名称,然后将方法的名称转换成NSString类型,存储到数组中一并返回。具体代码如下所示:

    5、获取协议列表

    下方是获取我们类所遵循协议列表的方法,主要使用了class_copyProtocolList()来获取列表,然后通过for循序使用protocol_getName()来获取协议的名称,最后将其转换成NSString类型放入数组中返回即可。

    6、动态添加方法实现

    7、方法实现交换

    属性的关联

    属性关联说白了就是在类目中动态的为我们的类添加相应的属性,如果看过之前发布的对Masonry框架源码解析的博客的话,对下方的属性关联并不陌生。在Masonry框架中就利用Runtime的属性关联在UIView的类目中给UIView添加了一个约束数组,用来记录添加在当前View上的所有约束。下方就是在TestClass的类目中通过objc_getAssociatedObject()和objc_setAssociatedObject()两个方法为TestClass类添加了一个dynamicAddProperty属性。上面我们获取到的属性列表中就含有该动态添加的成员属性。

     

  • 相关阅读:
    349元我们应该有什么样的期待原道N12豪华版 RK2906入手初体验
    漫谈国内智能手机市场现状
    将AltiumDesigner(Protel升级版)的PCB设计打造成利器——订制应用、操作、过滤表达式及其他一些微操作
    modelsim se 10.1a 下载与破解
    shell配置,选择,环境变量修改(ORACLE_HOME,ORACLE_SID),无法使用sqlplus
    /ibm/fan
    TrackPoint_configure_ThinkPad_squeeze(0616.2011)
    来个狠的
    JDBC for MSSql2005 简单示例
    oracle使用指南
  • 原文地址:https://www.cnblogs.com/guchengfengyun/p/8133172.html
Copyright © 2020-2023  润新知