美文网首页
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