三、方法(Method)的结构和调用

三、方法(Method)的结构和调用,第1张

Method 的结构非常简单,只包括方法名、类型以及函数指针。那 Method 又是存放在 Class 中哪里的呢?这里通过一张图来说明:

了解了 Method 的结构和存放地方之后,接下来说明下 Method 是如何随着 Class 的初始化而初始的,在经过一些断点调试之后,发现初始化的过程是在 static Class realizeClass(Class cls) 中初始化的。

realizeClass 中主要是将 rw 进行初始化, ro 的初始化过程则是在编译过程中已完成,无法看到对应的源码。之后我们通过打印自己的类来看一下这个初始化的过程(可以通过 lldb 中的 p 命令打印出结构体里的东西),首先我们新建了一个新类 TestObject :

class_addMethod 这个函数可以在运行期对类动态添加方法,我们来看下是怎么实现的:

向一个类中添加新方法, cls->data()->methods.attachLists(&newlist, 1) ,仍然是添加到 class_rw_t.methods 列表中。

我们知道 OC 中调用方法其实是发送消息,相当于调用 objc_msgSend(id self, SEL op, ...) ,但 objc_msgSend 不是开源的。但在上面动态添加方法 addMethod 中,可以看到首先通过 getMethodNoSuper_nolock 查找这个方法是否存在,我们可以猜测这个方法必定会在调用方法过程来调用,毕竟要先查询才能调用。通过查找调用 getMethodNoSuper_nolock 这个方法的地方和加断点,可以查到 objc_msgSend 发送消息后,接着调用 IMP lookUpImpOrForward(Class cls, SEL sel, bool initialize, bool cache, bool resolver) 查找方法并调用。

如果缓存中已缓存 sel ,那么直接返回对应的 imp 。

从当前类中找出 sel 对应的 method ,如果找到了则缓存起来并返回。下面是在当前类查找方法的实现:

查找过程非常简单,则列表中遍历查询。

如果在当前类中没有找到该方法对应的实现,那么将在继承链中查找这个方法。

如果在继承链中都无法找到该方法,那么将该方法决议(method resolve)。

最后一步是将进行转发,并且缓存。

在普通的方法中是不能调用构造方法的,但是在构造方法中可以调用其他的构造方法。

public class Test {

public Test(){

this("test")

//调用 Test(String str){}

//this()调用构造方法,通过参数来区分调用的是哪个构造方法。

//需要注意的就是,不可能出现递归调用的现象。

}

public Test(String str){

System.out.println(str)

}

}

在普通的方法中也没有调用构造方法的必要,如果是想调用构造方法中所写的代码,可以用如下方式。

public class Test {

public Test(){

this("test")

}

public Test(String str){

method1(str)

}

public void method1(String str){

System.out.println(str)

}

}

把构造方法中的代码写入一个方法中, 这样如果在想调用构造方法中的代码的话,直接调用method1就可以了。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11862549.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存