Runtime的个人感受

前言

RunTime简称运行时,是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API,平时编写的OC代码,在程序运行过程中,其实最终会转换成Runtime的C语言代码,Runtime是Object-C的幕后工作者,简单来说“就是系统在运行的时候的一些机制”

作用

那RunTime是怎么用的呢?

  1. 消息传递、转发
  2. 在程序运行过程中,可以动态的创建类,动态添加、修改这个类的属性和方法; (比如KVO的底层实现)
  3. 遍历一个类中所有的成员变量、属性、以及所有方法(如果你的成员变量私有,也可以获取到)

那应用的地方有哪些?

  • 给分类添加属性,方法
  • 方法的交换
  • 字典转模型
  • 获取对象的所有属性
  • 动态创建类
  • 当属性过多时,通过runtime便捷归解档
  • KVC、KVO,block
  • ...

相关概念

先了解类的本质和一些相关概念
Runtime简单粗暴理解

简单体验一下类底层代码、类的本质
将一段Object-C的代码用clang看下底层的C/C++的写法

typedef enum : NSUInteger { 
    Student = 0, 
    Worker = 1, 
    Programmer = 2,
 } 
ProfessionType; 

@interface Person : NSObject
@property (copy,nonatomic) NSString *name;
@property (assign,nonatomic) ProfessionType type; 

@end 

@implementation Person

 - (void)giveThePersonName:(NSString *)name{
      self.name = name;
}
 - (void)giveThePersonProgrammer:(ProfessionType)type{ 
      self.type = type; 
} 

@end

使用命令,在当前文件夹中会出现Person.cpp的文件
# clang -rewrite-objc Person.m

简单研读

/*
 * 顾名思义存放property的结构体
 * 当我们使用perproty的时候,会生成这样一个结构体
 * 具体存储的数据为 
 * 实际内容:"Name","T@\"NSString\",C,N,VName" 
 * 原型:@property (copy,nonatomic)NSString *Name;
 **/
struct _prop_t {
    const char *name;        //名字
    const char *attributes;  //属性
};

/*
 *类中方法的结构体,cmd和imp的关系是一一对应的关系
 *创建对象生成isa指针,指向这个对象的结构体时 
 *同时生成了一个表"Dispatch table"通过这个_cmd的编号找到对应方法
 *使用场景:
 *例如方法交换,方法判断。。。
 **/ 
struct _objc_method {
    struct objc_selector * _cmd;   //SEL 对应着OC中的@selector()
    const char *method_type;       //方法的类型
    void  *_imp;                   //方法的地址
}; 

/*
 * method_list_t 结构体:
 * 原型:
 * - (void)GiveThisGameName:(NSString *)name;
 * 实际存储的方式:
 * {(struct objc_selector *)"GiveThisGameName:", "v24@0:8@16", (void *)_I_Game_GiveThisGameName_}
 * 其主要目的是存储一个数组,基本的数据类型是 _objc_method
 * 扩展:当然这其中有你的属性,自动生成的setter、getter方法
 **/

static struct _method_list_t {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[6];
}

/*
 * 表示这个类中所遵守的协议对象
 * 使用场景:
 * 判断类是否遵守这个协议,从而动态添加、重写、交换某些方法,来达到某些目的
 * 
 **/

struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name;
    const struct _protocol_list_t * protocol_list; // super protocols
    const struct method_list_t *instance_methods;  // 实例方法
    const struct method_list_t *class_methods;     //类方法
    const struct method_list_t *optionalInstanceMethods;  //可选的实例方法
    const struct method_list_t *optionalClassMethods;  //可选的类方法
    const struct _prop_list_t * properties;  //属性列表
    const unsigned int size;  // sizeof(struct _protocol_t)
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;  //扩展的方法类型
};
  
/*
 * 类的变量的结构体
 * 原型:
* NSString *Name;
 * 存储内容:
* {(unsigned long int *)&OBJC_IVAR_$_Game$Name, "Name", "@\"NSString\"", 3, 8}
 * 根据存储内容可以大概了解这些属性的工作内容
 **/
  
struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;  //名字
    const char *type;  //属于什么变量
    unsigned int alignment; //未知
    unsigned int  size;    //大小
};

/*
 *  这个就是类中的各种方法、属性、等等信息
 *  底层也是一个结构体
 *  名称、方法列表、协议列表、变量列表、layout、properties。。
 *  
 **/

struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;  //布局
    const char *name;  //名字
    const struct _method_list_t *baseMethods;//方法列表 
    const struct _objc_protocol_list *baseProtocols; //协议列表
    const struct _ivar_list_t *ivars;  //变量列表
    const unsigned char *weakIvarLayout;  //弱引用布局
    const struct _prop_list_t *properties;  //属性列表
};

/*
 * 类本身
 * oc在创建类的时候都会创建一个 _class_t的结构体
* 我的理解是在runtime中的object-class结构体在底层就会变成_class_t结构体
 **/

struct _class_t {
    struct _class_t *isa;  //元类的指针
    struct _class_t *superclass; //父类的指针
    void *cache;   //缓存
    void *vtable;  //表信息、未知
    struct _class_ro_t *ro;  //这个就是类中的各种方法、属性、等等信息
};

/*
 * 类扩展的结构体
 * 在OC中写的类扩展
 **/

struct _category_t {
    const char *name;  //名称
    struct _class_t *cls;  //这个是哪个类的扩展
    const struct _method_list_t *instance_methods;  //实例方法列表
    const struct _method_list_t *class_methods;     //类方法列表
    const struct _protocol_list_t *protocols;       //协议列表
    const struct _prop_list_t *properties;          //属性列表
};

上述是Object-C中类中基本的数据,了解了类的定义,我们基本可以这么理解,类就是多个结构体组合的一个集合体,类中的行为、习惯、属性抽象,按照机器能懂的数据存储到我们底层的结构体当中,在我们需要使用的时候直接获取使用。

使用场景

  • 动态添加属性
 @implementation NSObject (Property)

- (void)setName:(NSString *)name
{  
  /*
     object:保存到哪个对象中
     key:用什么属性保存 属性名
     value:保存值
     policy:策略,strong,weak
     */
    objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, "name");
}
  • 动态添加方法
//在Person.m文件中

#import "Person.h"
#import 

@implementation Person

void eatFruit(id self, SEL _cmd) {

    NSLog(@"动态添加了一个吃水果的方法");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
      if (sel == NSSelectorFromString(@"eatFruit")) {
           class_addMethod(self, sel, (IMP)eatFruit, "v@:");
        return YES;
    }
    // 先恢复, 不然会覆盖系统的方法
    return [super resolveInstanceMethod:sel];
}

@end

//调用

    Person *man = [[Person alloc] init];

    [man performSelector:@selector(eatFruit)];

  • 交换方法

Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
Method Dm_imageNameMethod = class_getClassMethod(self, @selector(Dm_imageNamed:)); 
method_exchangeImplementations(imageNameMethod, Dm_imageNameMethod);

+ (instancetype)Dm_imageNamed:(NSString *)name { 
  // 这里调用Dm_imageNamed,相当于调用imageNamed 
  //可以根据自己的需求做处理
    UIImage *image = [self Dm_imageNamed:name];
     if (image == nil) {
         NSLog(@"加载空的图片"); 
     }

     return image; 
}

  • 字典转模型
/*  用一个分类来现实  */
//创建一个NSObject+Category 文件
//NSObject+Category.h

#import 
@interface NSObject (Category)
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary;
@end

//NSObject+Category.m文件

#import "NSObject+Category.h"
#import 
@implementation NSObject (Category)

+ (NSArray *)getPropertList
{  
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    NSMutableArray *array = [NSMutableArray array];
    for (int i = 0; i< count; i++) {
    //获取属性
    objc_property_t property = propertyList[i];
    //获取属性名称
    const char *pName = property_getName(property);
    NSString *name = [[NSString alloc] initWithUTF8String:pName];
    //添加到数组
    [array addObject:name];

    }
    //释放
    free(propertyList);
    return array.copy;

}

+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary
{
    id obj = [self new];
    // 遍历属性数组
    for (NSString *property in [self getPropertList]) {
        if (dictionary[property]) {
            // 使用 KVC 赋值
            [obj setValue:dict[property] forKey:property];
        }
    }
    return obj;
  }

@end
  


  • 快速归档解挡
//归档
- (void)encodeWithCoder:(NSCoder *)encoder {
     unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (NSInteger i = 0; i < count; i++) { 
          objc_property_t property = propertyList[i];
          //获取属性名称
          const char *pName = property_getName(property);
          NSString *name = [[NSString alloc] initWithUTF8String:pName];
          id value = [self valueForKey:name]; 
          [encoder encodeObject:value forKey:name]; 
      } 
      free(propertyList); //释放 
}

//解档
- (id)initWithCoder:(NSCoder *)decoder { 
     
    if (self = [super init]) {
     unsigned int count = 0;
      objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (NSInteger i = 0; i < count; i++) { 
          objc_property_t property = propertyList[i];
          //获取属性名称
          const char *pName = property_getName(property);
          NSString *name = [[NSString alloc] initWithUTF8String:pName];
          id value = [decoder decodeObjectForKey:name];
         [self setValue:value forKey:name];
      } 
      free(propertyList); //释放 

     return self; 

}


其实原理都是使用了RunTime的遍历属性,延展出来的各种操作。

结语

总体而已,RunTime在iOS中使用场景广泛,但并不难,只要理解了本质,就是成员变量,属性添加,查询,方法的替换等,只要遇到类似的问题,都可以从这几个方面去考虑,利用RunTime,高效的提升代码的延展性,高效快捷的处理问题。

引用的以下文章的部分内容
OC刨根问底】-Runtime简单粗暴理解
深入浅出Runtime (一) 什么是Runtime? 定义?
简述runtime的一些作用和使用场景

你可能感兴趣的:(Runtime的个人感受)