十一、方法的本质

1,398 阅读3分钟

本文由快学吧个人写作,以任何形式转载请表明原文出处

一、思路

在找对象的本质和类的本质的时候,是通过clang将.m文件编译成了c++的.cpp文件,查看编译后的代码,找到了对象的本质是结构体,类的本质是结构体objc_class : objc_object。

那么方法的本质也可以通过clang来查看。

二、创建项目

创建一个macos下的项目,便于测试。

  1. 创建一个类(我创建的是JDMan)继承于NSObject,并在JDMan中添加实例方法和类方法。
  2. 另外在main.m中引入runtime的头文件:<objc/message.h>
  3. 在main.m中创建一个JDMan的实例变量,并让这个实例变量调用实例方法。

创建详情如下 :

图片.png

图片.png

三、编译main.m

用terminal(终端)进入项目所在的文件夹再使用clang命令 :

clang -rewrite-objc main.m -o main.cpp

得到main.cpp文件,打开它。

图片.png

直接搜索自定义的实例方法,找到main函数里面 :

图片.png

四、方法的本质是否是objc_msgSend

第四节的图中看到alloc和zhuanQian两个方法中的共同点都是调用了objc_msgSend函数。那么objc_msgSend是否真的是方法的本质?验证一下是否正确。

思路 :

  1. 不调用任何的方法。只调用objc_msgSend。
  2. 如果方法的实现依然被调用了,那么就证明方法的本质就是objc_msgSend

1. objc_msgSend的参数

想调用objc_msgSend,就要知道它需要什么参数,在进入objc_msgSend(找到

图片.png

所以一个参数是:id self一个是SEL op。也就是说,一个是接收者,一个是方法索引SEL。

2. 只调用objc_msgSend发送信息

  1. 代码如下 :

图片.png

发现run起来会报错 : Too many arguments to function call, expected 0, have 2

原因是没有关闭objc_msgSend的严格检查。

  1. 关闭objc_msgSend的严格检查 :

图片.png

为什么要关闭这个检查?

因为objc_msgSend属于runtime的函数,本身是一个动态方法,中间会有很多的参数出现,如果想要静态的编译它,像C语言的函数一样,那就需要关闭检查。

  1. 正常运行

图片.png

这就可以证明实例方法的本质就是objc_msgSend消息发送。那么类方法呢?

五、类方法的本质是否一样

  1. 让类发送消息,通过objc_msgSend验证

图片.png

结果 :

图片.png

  1. 让类调用类方法,通过clang获得编译后的文件,查看类方法的调用的本质

用terminal(终端)进入项目所在的文件夹再使用clang命令 :

clang -rewrite-objc main.m -o main.cpp

得到main.cpp文件,打开它。

图片.png

结论是一样的 :

方法的本质就是objc_msgSend发送消息。

六、扩展一下

1. objc_msgSendSuper

objc_msgSend还有一个函数 : objc_msgSendSuper(),是子类调用父类的方法。

看一下objc_msgSendSuper的参数 :

图片.png

参数是struct objc_super *SEL

需要找开源源码818中去查找struct objc_super是什么。

图片.png

2. 项目更改

在原项目上再创建一个JDKid类,继承于JDMan。不定义任何的方法。

3. 子类调用父类的实例方法

图片.png

结果 :

图片.png

4. 子类调用父类的类方法

图片.png

结果 :

图片.png

5. 特殊情况

图片.png

结果 :

图片.png

为什么?

原因很简单 :isa的神图。一句话,自己没有找爹要。

图片.png

七、总结

方法的本质就是objc_msgSend消息发送。