本文由快学吧个人写作,以任何形式转载请标明原文出处
一、思路
在找目标的实质和类的实质的时分,是经过clang将.m文件编译成了c++的.cpp文件,检查编译后的代码,找到了目标的实质是结构体,类的实质是结构体objc_class : objc_object。
那么办法的实质也能够经过clang来检查。
二、创立项目
创立一个macos下的项目,便于测验。
- 创立一个类(我创立的是JDMan)继承于NSObject,并在JDMan中添加实例办法和类办法。
- 另外在main.m中引入runtime的头文件:
<objc/message.h>
。 - 在main.m中创立一个JDMan的实例变量,并让这个实例变量调用实例办法。
创立概况如下 :
三、编译main.m
用terminal(终端)进入项目所在的文件夹再运用clang指令 :
clang -rewrite-objc main.m -o main.cpp
得到main.cpp文件,翻开它。
直接查找自界说的实例办法,找到main函数里面 :
四、办法的实质是否是objc_msgSend
第四节的图中看到alloc和zhuanQian两个办法中的共同点都是调用了objc_msgSend
函数。那么objc_msgSend
是否真的是办法的实质?验证一下是否正确。
思路 :
- 不调用任何的办法。只调用objc_msgSend。
- 假如办法的实现仍然被调用了,那么就证明办法的实质便是
objc_msgSend
。
1. objc_msgSend的参数
想调用objc_msgSend,就要知道它需求什么参数,在进入objc_msgSend(
找到
所以一个参数是:id self
一个是SEL op
。也便是说,一个是接收者,一个是办法索引SEL。
2. 只调用objc_msgSend发送信息
- 代码如下 :
发现run起来会报错 : Too many arguments to function call, expected 0, have 2
。
原因是没有封闭objc_msgSend
的严格检查。
- 封闭
objc_msgSend
的严格检查 :
为什么要封闭这个检查?
因为objc_msgSend
归于runtime
的函数,自身是一个动态办法,中间会有许多的参数出现,假如想要静态的编译它,像C语言的函数相同,那就需求封闭检查。
- 正常运行
这就能够证明实例办法的实质便是objc_msgSend
音讯发送。那么类办法呢?
五、类办法的实质是否相同
- 让类发送音讯,经过
objc_msgSend
验证
成果 :
- 让类调用类办法,经过clang取得编译后的文件,检查类办法的调用的实质
用terminal(终端)进入项目所在的文件夹再运用clang指令 :
clang -rewrite-objc main.m -o main.cpp
得到main.cpp文件,翻开它。
结论是相同的 :
办法的实质便是
objc_msgSend
发送音讯。
六、扩展一下
1. objc_msgSendSuper
objc_msgSend
还有一个函数 : objc_msgSendSuper()
,是子类调用父类的办法。
看一下objc_msgSendSuper
的参数 :
参数是struct objc_super *
和SEL
需求找开源源码818中去查找struct objc_super
是什么。
2. 项目更改
在原项目上再创立一个JDKid类,继承于JDMan。不界说任何的办法。
3. 子类调用父类的实例办法
成果 :
4. 子类调用父类的类办法
成果 :
5. 特殊情况
成果 :
为什么?
原因很简单 :isa的神图。一句话,自己没有找爹要。
七、总结
办法的实质便是
objc_msgSend
音讯发送。