毕竟汇编语言代码比较晦涩难懂,因此这里将函数的实现反汇编成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函数的实现的更多相关文章

  1. 初探 objc_msgSend函数

    1.0 执行某个对象的方法    [receiver message] 被编译为: id objc_msgSend(id self,SEL op,...): objc_msgSend 发送信息的过程 ...

  2. 继承自NSObject的不常用又很有用的函数(2)

    函数调用 Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的.Selector相当于门牌号,而Implement才是真正的住户( ...

  3. 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 ...

  4. objc_msgSend消息传递学习笔记 – 消息转发

    该文是 objc_msgSend消息传递学习笔记 – 对象方法消息传递流程 的基础上继续探究源码,请先阅读上文. 消息转发机制(message forwarding) Objective-C 在调用对 ...

  5. objc_msgSend消息传递学习笔记 – 对象方法消息传递流程

    在Effective Objective-C 2.0 – 52 Specific Ways to Improve Your iOS and OS X Programs一书中,tip 11主要讲述了Ob ...

  6. 为什么objc_msgSend必须用汇编实现

    译者前言 总是看到有人说用汇编实现objc_msgSend是为了速度快,当然这个不可否认.但是难道没有别的原因?于是就看到了这篇文章,遂翻译之!=.= 我自己的理解就是,用汇编实现,是为了应对不同的“ ...

  7. OC语言的特性(一)-消息传递与调用函数的表现形式

    我们在初学Objective-C时,都会觉得ObjC中的消息传递和其他语言的调用函数差不多,只是在OC中,方法调用用消息传递这一概念来代替. 那么到底怎样区别OC中的消息传递与其他语言的调用函数呢. ...

  8. runtime objc_msgSend

    runtime objc_msgSend 字数1781 阅读245 评论2 喜欢7  前言 想要通过runtime发送消息,就必须要掌握runtime如何发送消息,是调用哪个函数?又是如何调用的?本篇 ...

  9. AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

    本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

随机推荐

  1. javascript正则表达式获取控制

    正则表达式的元字符是包含特殊含义的字符,他们有一些特殊的功能,可以控制匹配模式的方式,反斜杠后的元字符将失去其特殊含义 单个字符 元字符 匹配情况 . 匹配除换行符外的任意字符 [a-z0-9] 匹配 ...

  2. 转载:SQL按照日、周、月、年统计数据的方法

    转载源:http://www.jb51.net/article/42613.htm SQL按照日.周.月.季度.年统计数据的方法 方式一: --按日 select sum(consume),day([ ...

  3. [日常] go语言圣经-获取URL练习题

    1.主要使用net/http和io/ioutil包 2.http.Get函数是创建HTTP请求的函数,resp这个结构体中,Body字段包括一个可读的服务器响应流 3.ioutil.ReadAll函数 ...

  4. [WPF]记一个Win8"缩放级别"设置导致的问题

    这是我电脑的分辨率设置: 关键在于设置了缩放级别"较大",即150%的缩放. 接下来在WinForm中用各种方法取得的屏幕分辨率都是缩放之后的,但是这个时候的鼠标事件中鼠标位置也是 ...

  5. Redis到底是多线程还是单线程?线程安全吗

    redis是单线程,线程安全 redis可以能够快速执行的原因: (1) 绝大部分请求是纯粹的内存操作(非常快速)(2) 采用单线程,避免了不必要的上下文切换和竞争条件(3) 非阻塞IO - IO多路 ...

  6. 基于AlipayJSBridge封装的H5网页支付宝打赏、网站打赏、个人免签支付,支付宝转账打赏支付组件

    之前公司要做个打赏用户的功能,网站查询一些资料之后把一些api封装之后提供了一个demo组件供大家下载:扫描下图二维码 功能: 支付宝H5 Js方案,调起应用内页面,自动设定转账金额和收款理由,用户付 ...

  7. CSS 媒体查询创建响应式网站

    使用 CSS 媒体查询创建响应式网站  适用于所有屏幕大小的设计 固定宽度的静态网站很快被灵活的响应式设计所取代,该设计可以根据屏幕大小进行上扩和下扩.利用响应式设计,无论您采用什么设备或屏幕来访问网 ...

  8. 关于session销毁的问题,invalidate() 和removeAttribute()

    request.getSession().invalidate(); 销毁当前会话域中的所有属性 request.getSession().removeAttribute("username ...

  9. 【linux】扒站命令之利用wget快速扒站利用wget快速扒站

    在Linux下,通过一个命令就可以把整个站相关的文件全部下载下来. wget -r -p -k -np 参数说明: -r : 递归下载 -p : 下载所有用于显示 HTML 页面的图片之类的元素 -k ...

  10. Reset GitLab Root Password

    重置gitlab管理员密码 Log into your server with root privileges. Then start a Ruby on Rails console. Start t ...