iOS Runtime深入剖析

1.定义:
runtime是一套比较底层的纯C语言的API,属于一个C语言库,包含了很多底层的C语言API。
程序运行最终都是转成runtime的C语言代码。

2.whh?
runtime是属于OC的底层,可以进行一些非常底层的操作,OC无法实现。
在程序运行过程中,动态创建一个类(KVO的底层实现)。
在程序运行过程中,动态地为某一个类添加属性/方法。修改属性值方法。
遍历一个类的所有成员变量属性/所有方法。

3.用到的地方:数据转模型,对象归档,调试等

a.对象归档

//解档

//每次头文件都需要导入#import <objc/runtime.h>,每次都需要free,因为在Xcode中创建,没法自动释放C语言的内容,所以需要手动去做,第一个Ivar *ivars是要遍历出所有的这个类,而第二次不加*是因为只是取的其中一个实际的变量值。 

#import "Student.h"

#import <objc/runtime.h>


- (void)encodeWithCoder:(NSCoder *)aCoder{

    /**

     *  获得一个类的实例列表

     */

    unsigned int count = 0;

    //取出所有的实例变量

    Ivar  *ivars = class_copyIvarList([Student class], &count);

    for (int i = 0; i < count; i ++) {

        //指的是本身自己

        Ivar ivar = ivars[i];

        const char *name = ivar_getName(ivar);

        NSString  *ocName = [NSString stringWithUTF8String:name];

        id value = [self valueForKey:ocName];

        [aCoder encodeObject:value forKey:ocName];

    }

    free(ivars);

    

}

//归档

- (instancetype)initWithCoder:(NSCoder *)aDecoder{

 unsigned int count = 0;

    if (self = [super init]) {

       

        Ivar  *ivars = class_copyIvarList([Student class], &count);

        for (int i = 0; i < count; i ++) {

            //指的是本身自己

            Ivar ivar = ivars[i];

            const char *name = ivar_getName(ivar);

            

            NSString  *ocName = [NSString stringWithUTF8String:name];

            id value = [aDecoder decodeObjectForKey:ocName];

            [self setValue:value forKey:ocName];


        }

        free(ivars);

    }

    return self;

}


b.如何在一个代码中写好后,添加新的功能,比如需要加一个产品经理弄完后需要你在加一个统计一类的
//还是需要导入objc,然后需要在viewController里面定义一个,利用GCD的形式,只执行一次

#import "UIViewController+Swizzing.h"

#import <objc/runtime.h>

@implementation UIViewController (Swizzing)

/**

 *  引入类的时候调用

 */

+ (void)load{


}

/**

 *  调用类的方法之前调用

 */

+(void)initialize{

    //获得类的实例方法

    Method  originalMethod = class_getInstanceMethod([self class], @selector(viewWillAppear:));

    if (!originalMethod) {

        return;

    }


    Method swiMethod = class_getInstanceMethod([self class], @selector(customViewwillAppear:));

    if (!swiMethod) {

        return;

    }

    //交换方法


    method_exchangeImplementations(originalMethod, swiMethod);

}


/**

 *  1: customViewWillApear

    2:ViewwillApear

 *

 *  换成 

 2: customViewWillApear

 1:ViewwillApear


 */


# warning 调用的是ViewWIllApear  

- (void)customViewwillAppear:(BOOL)animated{


    static int count = 0;

    count++;

    NSLog(@"ViewwillAppear:%d",count);

    [self customViewwillAppear:animated];

}

//调用类的方法之前调用

+(void)initialize{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        //获得类的实例方法

        Method originalMethod = class_getInstanceMethod([self class], @selector(viewWillAppear:));

        if (!originalMethod) {

            return;

        }

        Method swiMethod = class_getInstanceMethod([self class], @selector(customViewWillAppear:));

        if (!swiMethod) {

            return;

        }

        //交换方法

        method_exchangeImplementations(originalMethod, swiMethod);

    });

}

- (void)customViewWillAppear:(BOOL)animated{

    static int count = 0;

    count ++;

    NSLog(@"viewWillAppear:%d",count);

    #warning 调用的是viewWillAppear:

    [self customViewWillAppear:animated];

}



c.数据转模型

#import "NSObject+Value.h"

#import <objc/message.h>

@implementation NSObject (Value)

- (void)setValues:(NSDictionary *)values

{

    Class c = [self class];

    

    while (c) {

        // 1.获得所有的成员变量

        unsigned int outCount = 0;

        Ivar *ivars = class_copyIvarList(c, &outCount);

        for (int i = 0; i<outCount; i++) {

            Ivar ivar = ivars[i];

            

            // 2.属性名

            NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];

            

            // 删除最前面的_

            [name replaceCharactersInRange:NSMakeRange(0, 1) withString:@""];

            

            // 3.取出属性值

            NSString *key = name;

            //得到字典对应keyvalue

            id value = values[key];

            if (!value) continue;

            // 4.SEL

            // 首字母

            NSString *cap = [name substringToIndex:1];

            // 变大写

            cap = cap.uppercaseString;

            // 将大写字母调换掉原首字母

            [name replaceCharactersInRange:NSMakeRange(0, 1) withString:cap];

            // 拼接set

            [name insertString:@"set" atIndex:0];

            // 拼接冒号:

            [name appendString:@":"];

            SEL selector = NSSelectorFromString(name);


            // 5.属性类型

            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

            #warning  ios8不能使用:build settings -> enable strict checking of objc_mssend 改为No


            if ([type hasPrefix:@"@"]) { // 对象类型

//                objc_msgSend(self, selector, value);

                ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, selector, value);

            } else  { // 非对象类型

                if ([type isEqualToString:@"d"]) {

                    objc_msgSend(self, selector, [value doubleValue]);

                } else if ([type isEqualToString:@"f"]) {

                    objc_msgSend(self, selector, [value floatValue]);

                } else if ([type isEqualToString:@"i"]) { 

                    objc_msgSend(self, selector, [value intValue]);

                }  else { 

                    objc_msgSend(self, selector, [value longLongValue]);

                }

            }

        }

        

        c = class_getSuperclass(c);

    }

}

@end

在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。因为C语言的执行顺序是从上到下,举例,就像一个button 在OC中需要点击才会调用里面的方法,而C语言是先访问一次,有时候点击Button会直接出现崩溃,但是在C语言中,直接就没办法执行。


相关函数:

  • objc_msgSend : 给对象发送消息
  • class_copyMethodList : 遍历某个类所有的方法
  • class_copyIvarList : 遍历某个类所有的成员变量

上面的函数都是经常被调用的。

以上的代码是运行在Xcode7.0版本上面的,需要配置的地方都在代码有详细的解释,如果需要转载,请注明出处






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值