iOS|iOS objc_msgSend 伪代码
【iOS|iOS objc_msgSend 伪代码】如题,记录下学习runtime底层一些汇编代码的解读
//
//objc_msgSend反汇编伪代码.h
//Test
//
//Created by kinglee on 2019/12/13.
////下面的结构体中只列出objc_msgSend函数内部访问用到的那些数据结构和成员。/*
其实SEL类型就是一个字符串指针类型,所描述的就是方法字符串指针
*/
typedef char * SEL;
/*
IMP类型就是所有OC方法的函数原型类型。
*/
typedef id (*IMP)(id self, SEL _cmd, ...);
/*
方法名和方法实现桶结构体
*/
struct bucket_t{
SELkey;
//方法名称
IMP imp;
//方法的实现,imp是一个函数指针类型
};
/*
用于加快方法执行的缓存结构体。这个结构体其实就是一个基于开地址冲突解决法的哈希桶。
*/
struct cache_t {
struct bucket_t *buckets;
//缓存方法的哈希桶数组指针,桶的数量 = mask + 1
intmask;
//桶的数量 - 1
intoccupied;
//桶中已经缓存的方法数量。
};
/*
OC对象的类结构体描述表示,所有OC对象的第一个参数保存是的一个isa指针。
*/
struct objc_object {
void *isa;
};
/*
OC类信息结构体,这里只展示出了必要的数据成员。
*/
struct objc_class : objc_object {
struct objc_class * superclass;
//基类信息结构体。
cache_t cache;
//方法缓存哈希表
//... 其他数据成员忽略。
};
/*
objc_msgSend的C语言版本伪代码实现.
receiver: 是调用方法的对象
op: 是要调用的方法名称字符串
*/
idobjc_msgSend(id receiver, SEL op, ...)
{//1............................ 对象空值判断。
//如果传入的对象是nil则直接返回nil
if (receiver == nil)
return nil;
//2............................ 获取或者构造对象的isa数据。
void *isa = NULL;
//如果对象的地址最高位为0则表明是普通的OC对象,否则就是Tagged Pointer类型的对象
if ((receiver & 0x8000000000000000) == 0) {
struct objc_object*ocobj = (struct objc_object*) receiver;
isa = ocobj->isa;
}
else { //Tagged Pointer类型的对象中没有直接保存isa数据,所以需要特殊处理来查找对应的isa数据。//如果对象地址的最高4位为0xF, 那么表示是一个用户自定义扩展的Tagged Pointer类型对象
if (((NSUInteger) receiver) >= 0xf000000000000000) {//自定义扩展的Tagged Pointer类型对象中的52-59位保存的是一个全局扩展Tagged Pointer类数组的索引值。
intclassidx = (receiver & 0xFF0000000000000) >> 52
isa =objc_debug_taggedpointer_ext_classes[classidx];
}
else {//系统自带的Tagged Pointer类型对象中的60-63位保存的是一个全局Tagged Pointer类数组的索引值。
int classidx = ((NSUInteger) receiver) >> 60;
isa=objc_debug_taggedpointer_classes[classidx];
}
}//因为内存地址对齐的原因和虚拟内存空间的约束原因,
//以及isa定义的原因需要将isa与上0xffffffff8才能得到对象所属的Class对象。
struct objc_class*cls = (struct objc_class *)(isa & 0xffffffff8);
//3............................ 遍历缓存哈希桶并查找缓存中的方法实现。
IMPimp = NULL;
//cmd与cache中的mask进行与计算得到哈希桶中的索引,来查找方法是否已经放入缓存cache哈希桶中。
int index =cls->cache.mask & op;
while (true) {//如果缓存哈希桶中命中了对应的方法实现,则保存到imp中并退出循环。
if (cls->cache.buckets[index].key == op) {
imp = cls->cache.buckets[index].imp;
break;
}//方法实现并没有被缓存,并且对应的桶的数据是空的就退出循环
if (cls->cache.buckets[index].key == NULL) {
break;
}//如果哈希桶中对应的项已经被占用但是又不是要执行的方法,则通过开地址法来继续寻找缓存该方法的桶。
if (index == 0) {
index = cls->cache.mask;
//从尾部寻找
}
else {
index--;
//索引减1继续寻找。
}
} /*end while*///4............................ 执行方法实现或方法未命中缓存处理函数
if (imp != NULL)
return imp(receiver, op,...);
//这里的... 是指传递给objc_msgSend的OC方法中的参数。
else
return objc_msgSend_uncached(receiver, op, cls, ...);
}/*
方法未命中缓存处理函数:objc_msgSend_uncached的C语言版本伪代码实现,这个函数也是用汇编语言编写。
*/
id objc_msgSend_uncached(id receiver, SEL op, struct objc_class *cls)
{
//这个函数很简单就是直接调用了_class_lookupMethodAndLoadCache3 来查找方法并缓存到struct objc_class中的cache中,最后再返回IMP类型。
IMPimp =_class_lookupMethodAndLoadCache3(receiver, op, cls);
return imp(receiver, op, ....);
}
推荐阅读
- 2020-04-07vue中Axios的封装和API接口的管理
- iOS中的Block
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片
- 2019-08-29|2019-08-29 iOS13适配那点事
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- iOS面试题--基础
- 接口|axios接口报错-参数类型错误解决
- iOS|iOS 笔记之_时间戳 + DES 加密
- iOS,打Framework静态库
- 常用git命令总结