美文网首页
Runtime的应用

Runtime的应用

作者: sunny_王 | 来源:发表于2017-09-10 12:18 被阅读10次

来自:http://www.imlifengfeng.com/blog/?p=397
runtime是运行时库(Runtime Library),也简称运行时。
它是一个主要是C和汇编写的库,对C进行了特殊的处理,将结构体视为对象,将函数视为方法,使得C有了面向对象的能力,从而才创造了Objective-C。
这点也可以看出,C是编译时语言,而OC是动态运行时语言,所以在编译阶段,尽管OC中的方法没有实现也不会报错,而C会报错。
在运行时,OC语言才进行方法的处理,比如讲[person eat];转换为objc_msgSend(person, @selector(eat));然后通过person的isa指针找到person对应的class,在class中先去cache中通过SEL方法选择器查找对应的method,若缓存中未找到,再去methodList查找,若还未找到,便去父类中查找,如果这个过程中找到了该方法,便会自动将该方法加入cache中,方便下一次的查找,并且,编译器开始执行找到的这个函数。
1、快速归档

  • (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];
    }
    }

2、Json到Model的转化

  • (instancetype)initWithDict:(NSDictionary *)dict {

    if (self = [self init]) {
    //(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];
      }
    

    }
    return self;
    }

3、Category添加属性并生成getter和setter方法

import <Foundation/Foundation.h>

@interface NSArray (MyCategory)
//不会生成添加属性的getter和setter方法,必须我们手动生成
@property (nonatomic, copy) NSString *blog;
@end

import "NSArray+MyCategory.h"

import <objc/runtime.h>

@implementation NSArray (MyCategory)

// 定义关联的key
static const char *key = "blog";

/**
blog的getter方法
*/

  • (NSString *)blog
    {
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, key);
    }

/**
blog的setter方法
*/

  • (void)setBlog:(NSString *)blog
    {
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, blog, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    @end

4、Method Swizzling使用

import "UIViewController+swizzling.h"

import <objc/runtime.h>

@implementation UIViewController (swizzling)

  • (void)load {
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));
    /**
    • 我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
    • 而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
    • 所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
      */
      if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
      method_exchangeImplementations(fromMethod, toMethod);
      }
      }

// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。

  • (void)swizzlingViewDidLoad {
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉
    if(![str containsString:@"UI"]){
    NSLog(@"统计打点 : %@", self.class);
    }
    [self swizzlingViewDidLoad];
    }
    @end

5、Method Swizzling类簇
__NSArrayI才是NSArray真正的类

import "NSArray+ MyArray.h"

import "objc/runtime.h"

@implementation NSArray MyArray)

  • (void)load {//是加载文件到内存执行的方法 跟类的生命周期没有关系
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(my_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
    }
  • (id)my_objectAtIndex:(NSUInteger)index {
    if (self.count-1 < index) {
    // 这里做一下异常处理,不然都不知道出错了。
    @try {
    return [self my_objectAtIndex:index];
    }
    @catch (NSException *exception) {
    // 在崩溃后会打印崩溃信息,方便我们调试。
    NSLog(@"---------- %s Crash Because Method %s ----------\n", class_getName(self.class), func);
    NSLog(@"%@", [exception callStackSymbols]);
    return nil;
    }
    @finally {}
    } else {
    return [self my_objectAtIndex:index];
    }
    }
    @end

6、runtime添加类方法
[objc] view plain copy
void study(id reccevier, SEL sel) {
}
// 如果调用的方法没有实现,就会走这个方法

  • (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(study)) {
    class_addMethod(self, @selector(study), (IMP)study, "v@:");
    }
    return [super resolveInstanceMethod:sel];
    }
    值得注意的是,这个方法是在运行时动态调用的,所以编译的时候会有警告。

相关文章

网友评论

      本文标题:Runtime的应用

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