本文由快学吧个人写作,以任何形式转载请表明原文出处
一、思路
在找对象的本质和类的本质的时候,是通过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
消息发送。