了解了 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就可以了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)