概述
相比C语言在编译阶段就已经确定了所有变量的数据类型及要调用的函数,OC可以在运行阶段检查变量的类型查找具体函数。
消息机制
-
对象方法调用过程:
- 编译阶段, oc对象调用方法被编译成
obj_msgsend - 运行阶段,实例对象的调用过程:
- 通过
isa_t找到Class,即object_class *,参数都放到寄存器中。
isa_t在64bit下是个BITFIELD,其他的信息包括是否有关联对象,有无C++代码,当前类地址掩码shiftcls(33位,用于取出存储对象地址), 是否被其他对象弱引用weakly_referenced,是否当前引用计数过大,需要在单独的sidetable中存储has_sidetable_rc, 引用计数器extra_rc- 通过
object_class.objc_cache,找对应的method(selector+IMP),找到执行,找不到goto 3 - 在
method_list中找对应的method,找到后填充到cache 中,找到执行,找不到goto 4 - 在
superClass中查找 - 消息转发(详见3)
- 通过
- 编译阶段, oc对象调用方法被编译成
-
类方法的调用过程:
- 通过
isa找到MetaClass - 通过元类MetaClass
method _list找到对应method并执行meta_class保存了类方法, class存储的是实例化的对象方法,他们都存储在isa_t
- 消息转发(详见3)
- 通过
-
消息转发机制:
- 消息动态解析
resolveInstanceMethod+resolveClassMethod - 消息接受者重定向
forwardingTargetForSelector - 消息重定向
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
通过消息转发动态添加或替换方法实现热修复