Runtime底层原理分析

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

官方:
url: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048

OC对象、方法

  1. OC对象的本质是结构体;
  2. 方法的本质是消息发送;
  3. 消息的组成:
main.m 复制代码
Person *p = [[Person alloc] init];
[p run];

本质:

main.mm 复制代码
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run"))
  • 消息接收者:p
  • 方法:sel_registerName("run") -> 方法编号 -- 字符串name
    通过sel 找到函数实现的指针.
    函数实现类似于:
复制代码
void runIMP(id self, SEL _cmd) {
    ...
}

Runtime调用的3种方式

  1. runtime api
  2. NSObject api isKindOf, isMemberOf
  3. 编译器提供的oc上层方法 @selector
  4. 向父类发消息
  • 对象方法:
复制代码
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));
  • 类方法:
复制代码
struct objc_super myClassSuper;
myClassSuper.receiver = [s class];
myClassSuper.super_class = class_getSuperclass(object_getClass([s class));  // 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));
  • 类方法存在元类之中,以实例方法的姿态形式存在
  • 对象在类中是一个实例,类在元类中也是一个实例

objc_msgSend

  1. 快速
  • 缓存找 汇编 -> cache_t 找imp通过hash表
  • cache在类结构体中
  1. 慢速
    直接通过C, C++, 汇编来找. 找到后存缓存
  • objc_msgSend用汇编来写
  • 如果用C函数,不能写一个函数,跳转到任意的指针
  • C还要往下层编译,汇编快
  • 汇编有寄存器, x0 - x31

源码分析

tagged pointer特殊的数据类型

  1. _objc_msgSend
  2. LNilOrTagged
  3. LGetIsaDone isa处理完毕
  4. CacheLoopup NORMAL
  • call imp

  • objc_msgSend_uncached

  • CacheHit

  • CheckMiss -> _objc_msgSend_uncached

  • add

additional: bl汇编指令就是跳转.

MethodTableLookup

C、C++:

  1. lookUpImpOrForward
  2. relizeClass(cls)
  3. _class_initialize
  4. 漫长的过程 -> 找方法 -> 找自己 -> 找父类 -> NSObject
  • getMethodNoSuper_nolock 递归查找方法列表中的方法

  • log_and_fill_cache

  • remap(cls)重映射

  1. resolver动态解析
    如果方法没有实现,走resolver方法.
    动态解析一次.
    流程:
  • lookup imp
  • 如果没有,走_class_resolveMethod(只走一次)
  • 如果不是元类,走_class_resolveInstanceMethod
  • 如果是元类,走_class_resolveClassMethod,再_class_resolveInstanceMethod
  1. 消息转发forwardingTargetForSelector
  2. 方法签名
  3. 无法处理doesNotRecognize

查看所有的objc_msgSend信息的函数

复制代码
extern void instrumentobjcMessageSends(BOOL);

可以在private -> tmp -> msgSends-进程号

总结

runtime就是可以做到

  1. 动态添加注册类
  2. 查询/添加属性、方法

暂无评论,快来发表第一条评论吧