IOS之Runtime

155 阅读3分钟

概述

相比C语言在编译阶段就已经确定了所有变量的数据类型及要调用的函数,OC可以在运行阶段检查变量的类型查找具体函数。

消息机制

  1. 对象方法调用过程:

    1. 编译阶段, oc对象调用方法被编译成 obj_msgsend
    2. 运行阶段,实例对象的调用过程:
      1. 通过isa_t找到Class,即object_class *参数都放到寄存器中。

      isa_t在64bit下是个BITFIELD,其他的信息包括是否有关联对象,有无C++代码,当前类地址掩码shiftcls(33位,用于取出存储对象地址), 是否被其他对象弱引用weakly_referenced,是否当前引用计数过大,需要在单独的sidetable中存储has_sidetable_rc, 引用计数器extra_rc

      1. 通过object_class.objc_cache,找对应的method(selector+IMP),找到执行,找不到goto 3
      2. method_list中找对应的method,找到后填充到cache 中,找到执行,找不到goto 4
      3. superClass中查找
      4. 消息转发(详见3)
  2. 类方法的调用过程:

    1. 通过isa找到MetaClass
    2. 通过元类MetaClass method _list找到对应method并执行

      meta_class保存了类方法, class存储的是实例化的对象方法,他们都存储在isa_t

    3. 消息转发(详见3)
  3. 消息转发机制:

    1. 消息动态解析 resolveInstanceMethod + resolveClassMethod
    2. 消息接受者重定向 forwardingTargetForSelector
    3. 消息重定向 methodSignatureForSelector + forwardInvocation

runtime关键概念解析

类(Class) && 对象(id)

类Class是指向objc_class结构体的指针,包含了 isa/superClass/cache/protocols/methodList/ivars等

对象id 是指向objc_object结构体的指针

//object_class

struct objc_class {

    Class/isa_t isa; //实例对象 isa 指向类对象Class, 类对象isa指向meta-class, 基类meta-class isa指向自己

    Class/isa_t _Nullable super_class  //类对象class如果没有父类superclass=nil, 基类元类对象meta-class的superclass指向基类的类对象class

    ...;
    
    struct objc_ivar_list * _Nullable ivars ;

    struct objc_method_list * _Nullable * _Nullable methodLists ;

    struct objc_cache * _Nonnull cache;

    struct objc_protocol_list * _Nullable protocols ;

};
// id-objc_object
struct objc_object {
    isa_t _Nonnull isa  OBJC_ISA_AVAILABILITY; //对应的类对象
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

IMP && SEL && Method

typedef struct objc_selector *SEL; //objc_selector保存了方法名字符串

typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); //本质是一个函数指针, IMP就是函数地址

typedef struct objc_method *Method;

struct objc_method {

    SEL _Nonnull method_name;                    // 方法名

    char * _Nullable method_types;               // 方法类型

    IMP _Nonnull method_imp;                     // 方法实现

};

Method 将 SEL(方法名) 和 IMP(函数指针) 关联起来,当对一个对象发送消息时,会通过给出的 SEL(方法名) 去找到 IMP(函数指针)

Runtime应用场景

Category && Extension

Category 是利用runtime实现, 运行时阶段动态地为已有类添加新行为,无法添加成员变量,方法、属性、协议会被添加到原有类的方法列表、属性列表、协议列表的最前面,而原有类的方法、属性、协议则被移动到了列表后面

Extension是编译期间做的工作:

  • 声明的方法只能在该类的 @implementation 中实现

  • 不但可以声明方法,还可以声明成员变量;

  • 在编译阶段与该类同时编译的,是类的一部分

Method Swizzling

load方法dispatc_once 通过class_replaceMethod将A方法替换为指定的B方法. 比如可以用于APM监控,CRASH防护,埋点等

KVO

当观察对象A时,在KVO 动态创建对象当前类的子类NSKVONotifying_A,并重写被观察属性 setter 方法,setter 方法通过willChange/.../didChange通知值的变化. 当我们想自定义是否通知时,可以重写automaticallyNotifiesObserversForKey方法。

JSON<->Model转化

NSObject的分类添加initWithDic,遍历Model属性,到json中查找对应key值赋值.

Patch

通过消息转发动态添加或替换方法实现热修复