objc_msgSend函数的实现
毕竟汇编语言代码比较晦涩难懂,因此这里将函数的实现反汇编成C语言的伪代码:
//下面的结构体中只列出objc_msgSend函数内部访问用到的那些数据结构和成员。
/*
其实SEL类型就是一个字符串指针类型,所描述的就是方法字符串指针
*/
typedef char * SEL;
/*
IMP类型就是所有OC方法的函数原型类型。
*/
typedef id (*IMP)(id self, SEL _cmd, ...);
/*
objc_msgSend的C语言版本伪代码实现.
receiver: 是调用方法的对象
op: 是要调用的方法名称字符串
*/
id objc_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类数组的索引值。
int classidx = (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............................ 遍历缓存哈希桶并查找缓存中的方法实现。
IMP imp = 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类型。
IMP imp = _class_lookupMethodAndLoadCache3(receiver, op, cls);
return imp(receiver, op, ....);
}
https://www.jianshu.com/p/df6629ec9a25
objc_msgSend函数的实现的更多相关文章
- 初探 objc_msgSend函数
1.0 执行某个对象的方法 [receiver message] 被编译为: id objc_msgSend(id self,SEL op,...): objc_msgSend 发送信息的过程 ...
- 继承自NSObject的不常用又很有用的函数(2)
函数调用 Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的.Selector相当于门牌号,而Implement才是真正的住户( ...
- objc_msgSend()报错Too many arguments to function call ,expected 0,have3
Build Setting--> Apple LLVM 6.0 - Preprocessing--> Enable Strict Checking of objc_msgSend Call ...
- objc_msgSend消息传递学习笔记 – 消息转发
该文是 objc_msgSend消息传递学习笔记 – 对象方法消息传递流程 的基础上继续探究源码,请先阅读上文. 消息转发机制(message forwarding) Objective-C 在调用对 ...
- objc_msgSend消息传递学习笔记 – 对象方法消息传递流程
在Effective Objective-C 2.0 – 52 Specific Ways to Improve Your iOS and OS X Programs一书中,tip 11主要讲述了Ob ...
- 为什么objc_msgSend必须用汇编实现
译者前言 总是看到有人说用汇编实现objc_msgSend是为了速度快,当然这个不可否认.但是难道没有别的原因?于是就看到了这篇文章,遂翻译之!=.= 我自己的理解就是,用汇编实现,是为了应对不同的“ ...
- OC语言的特性(一)-消息传递与调用函数的表现形式
我们在初学Objective-C时,都会觉得ObjC中的消息传递和其他语言的调用函数差不多,只是在OC中,方法调用用消息传递这一概念来代替. 那么到底怎样区别OC中的消息传递与其他语言的调用函数呢. ...
- runtime objc_msgSend
runtime objc_msgSend 字数1781 阅读245 评论2 喜欢7 前言 想要通过runtime发送消息,就必须要掌握runtime如何发送消息,是调用哪个函数?又是如何调用的?本篇 ...
- AFNetworking 3.0 源码解读(五)之 AFURLSessionManager
本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...
随机推荐
- 微信开放平台Android应用的签名
微信开放平台Android应用签名的本质便是我们签名文件keystore的MD5值. keytool -list -v -keystore qj_test.keystore 获得: 别名: naoli ...
- java基础之基础语法详录
[前言] java的语法先从基础语法学,Java语言是由类和对象组成的,其对象和类又是由方法和变量组成,而方法,又包含了语句和表达式. 对象:(几乎)一切都是对象,比如:一只熊猫,他的外观,颜色,他在 ...
- 深入分析 Java 中的中文编码问题【转】
转:https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/ 几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是 ...
- 【转】ArrayBlockingQueue浅析
ArrayBlockingQueue是常用的线程集合,在线程池中也常常被当做任务队列来使用.使用频率特别高.他是维护的是一个循环队列(基于数组实现),循环结构在数据结构中比较常见,但是在源码实现中还是 ...
- CSS3 linear-gradient线性渐变实现虚线等简单实用图形
一.作为图片存在的CSS3 gradient渐变 我觉得CSS3 Backgrounds比较厉害的一个地方就是支持多背景,也就是背景图片个数可以无限累加,正好CSS3的gradient渐变性质是bac ...
- SpringMVC+MyBatis+MySQL 8小时链接断开
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is ...
- 分享到xxx
来源百度 一.概述 百度分享代码已升级到2.0,本页将介绍新版百度分享的安装配置方法,请点击左侧列表查看相关章节. 二.代码结构 分享代码可以分为三个部分:HTML.设置和js加载,示例如下: 代码结 ...
- Spring Boot—20Zookeeper
https://docs.spring.io/spring-boot/docs/2.0.1.RELEASE/reference/htmlsingle/ pom.xml <dependency&g ...
- Jupyter notebook 使用多个Conda 环境
conda install nb_conda_kernels
- easyui 笔记
easyui-datagrid:loadFilter:处理服务器端传递过来的参数. 刷新datagrid:$("#xxx").datagrid('reload'): form 表单 ...