runtime-对象和方法

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

oc文件->.cpp文件:

复制代码
clang -rewrite-objc main.m -o main.cpp
  • 完整命令:
复制代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.c++
  1. 方法的本质 -> 消息发送
    objc:
复制代码
- (void)run { }

对应底层C:

复制代码
void run(id self, SEL _cmd) { }
  1. 方法: 对象方法和类方法.

  2. runtime的调用方式:三种:

  • runtime api: class_get...
  • NSObject api: isKindOf isMemberOf
  • oc上层方法: @selector
  1. 对象方法的调用:
复制代码
Object *obj = [Object new];
[obj run];
  • 方法调用的底层编译
  • 方法的本质: 消息: 消息接受者 消息编号 ...参数(消息体)
复制代码
objc_msgSend(obj, sel_registerName("run"));
  1. 类方法编译底层
复制代码
id cls = [ObjectClass class];
void *pointA = &cls;
[(__bridge id)pointA walk];

===

复制代码
objc_msgSend(objc_getClass("ObjectClass"), sel_registerName("walk"));
  • 向父类发消息(对象方法):
复制代码
struct objc_super mySuper;
mySuper.receiver = obj;
mySuper.super_class = class_getSuperclass([obj class);
objc_msgSendSuper(&mySuper, @selector(run));
  • 向父类发消息(类方法):
复制代码
struct objc_super myClassSuper;
myClassSuper.receiver = [obj class];
myClassSuper.super_class = class_getSuperClass(object_getClass([obj class])); // 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("walk"));
  1. 对象方法存在类中

  2. 类方法存在元类中

  3. 类方法在元类中,以什么样的姿态存在?
    :以实例方法存在.

  4. 对象在类中是以实例存在.

  5. 类在元类中也是以实例形式存在.

  6. objc_msgSend
    两种方式:

  • 快速 by汇编 缓存找 cache_t imp 哈希表
  • 慢速 c, c++一起完成,找到存缓存

否则 经过复杂的过程
为什么使用汇编写?

  • c 写一个函数,不可能保留未知的参数,跳转到任意的指针.
  • 块,c还要向下编译
  • 汇编可以保存,by 寄存器 x0 x31
  1. 源码分析流程
  • 汇编部分
  • c,c++部分
    具体查看 opensource.apple.com -> objc4
    分析源码的文件是objc4中arm64相关的.s汇编文件,然后汇编中有__class_lookupMethodAndLoadCache3一个宏调用,要在工程中使用_class_lookupMethodAndLoadCache3来查找,因为在.mm文件里面.

大概的过程:

  • 消息发送调用后,通过汇编,从汇编的缓存中查找
  • 如果找到,那么就直接调用,否则查找方法实现的函数指针,找到实现函数指针,就返回函数指针
  • 如果在缓存中找不到,也找不到函数指针,那么通过c、c++函数去找,先是自己的类中找,然后递归从父类中查找。
  • 如果找到,那么就fill cache并且返回这个函数指针。找不到的话,那么就要开始走动态解析了。

-- Warning:
在c、c++的查找过程中,会再次判断一下缓存中是否有函数实现,尽管汇编过程中已经判断过缓存。这个原因是:oc语言的动态性,并发,有可能使缓存中有值了。

  1. 动态方法解析.
    类的方法解析:(动态方法解析的函数名一样,只是前缀是-,而不是+)
复制代码
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    return [super resolveinstanceMethod: sel];
}
复制代码
+ (BOOL)resolveClassMethod:(SEL)sel {
    return [super resolveClassMethod: sel];
}

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