msgSend 流程详解

1.编译阶段

用户调用一个普通函数
[anObject doTings:things];
编译器会把其编译为:
objc_msgSend(anObject, @selector(doThings:), things);
注意:根据函数返回值的不同,编译器还会将其编译为objc_msgSend_stret、objc_msgSend_fpret等。但是,实现大同小异。这里只以objc_msgSend为例进行讨论。

2.msgSend概述

objc_msgSend实现大致如下:
id objc_msgSend(id self, SEL _cmd, ...){
Class class = object_getClass(self;)
IMP imp = cache_lookup(c, _cmd);
if(!imp){
imp = class_getMethodImplementation(class, _cmd);
}
return imp ? imp(self, _cmd, ...) : 0
}

由此可见,其主要功能可分为获取函数imp和调用imp两块。

3.msgSend整体流程

screenshot


Step1. dispatch table中搜索selector的imp。这步大家应该都清楚,子类找不到,就去搜索父类,依次往上遍历。
注:dispatch table是编译时生成的selector与imp的对应表

Step2. 如果子类和所有父类的dispatch table中都没找到,就到动态解析。
很多人可能认为,这个曲折的寻找过程是必不可少的。其实并不是这样的。可以将子类中某个方法的imp直接设置成objc_msgForward,就可以直接进入转发流程。

Step3.函数调用
这里着重介绍下转发机制(__forwarding__)的实现。其代码大致如下:

screenshot

这里不做详细分析,简要说下我认为有用的几点:
a.为何有了forwardingInvoaction,还需要fowardingTarget?
因为fowardingTarget不涉及参数解析与封装,只是简单的把target替换下,就立马进入下一轮msgSend,所以效率较高,是首选。只有当涉及参数处理时,才有必要在forwardingInvoaction中干。

b.为什么forwardingInvoaction前,需要获取methodSignature?
单靠selector,无法完成对参数的解析,从而无法完成对target、selector和参数的打包。此时,需要获取method signature(包含参数、返回值的所有信息)来协助完成对参数的解析。


objc_msgForward和其他函数imp一样,都是函数指针
objc_msgForward和其他函数imp一样,都是函数指针
objc_msgForward和其他函数imp一样,都是函数指针
重要的事情说三遍

参考文献

[1] What's that Selector?
[2] Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值