Open hyice opened 6 years ago
给NSObject加了一个-test方法
这里是不是应该是添加一个+test
添加类方法最后找到的是从NSObject继承过来的实例方法
给NSObject加了一个-test方法
这里是不是应该是添加一个+test
添加类方法最后找到的是从NSObject继承过来的实例方法
居然有人看!!!
给NSObject
加的就是实例方法- test
。然后后面调用的却是类方法 [NSObject test]
。这时候直观来看,NSObject
实际上是没有+ test
方法的(或者说是RootMetaClass
上没有- test
方法),应该报错才对。但实际上RootMetaClass
的父类就是NSObject
,所以会继承到这个方法。
在开始本篇的具体内容前,大家需要对上图表达的东西有个大概的了解,大家可以看一下这篇文章的 Class 部分。我这里简单罗列下之后会重点涉及的几个概念:
MetaClass
,对类方法的调用,实际上是对该MetaClass
实例方法的调用。也就是说,当我们对一个类调用类方法时,实际上是去获取了MetaClass
全局且唯一的实例对象,然后在该实例上调用了实例方法。ChildClass
的父类是ParentClass
,那么ChildMetaClass
的父类就是ParentMetaClass
。但NSObject
的父类为nil
,而它的MetaClass
的父类却是NSObject
。也就是说,NSObject
基本上可以说是所有类的根父节点,当然也有例外,如NSProxy
。NSObject
的MetaClass
称呼为RootMetaClass
,之所以我敢这么称呼,是因为它真的是根节点,连它自己的MetaClass
都是自己。好了,如果你已经完全理解我上面说的几个概念,那么我们就开始今天的主题吧。
NSObject的类方法与实例方法
首先,我们来聊聊
NSObject
上的类方法与实例方法。相信你这时候应该很困惑,这有什么好聊的,不就是我们上面提到过的一个基本概念里的内容嘛。没错,对于大部分的类来说,这两者就是这么泾渭分明。只要你理解清楚了
MetaClass
的概念,这里并不会有什么理解障碍。但对NSObject
和RootMetaClass
来说,类方法和实例方法并没有这么泾渭分明。假设我们现在通过
Category
给NSObject
加了一个-test
方法,那么我们如果进行[NSObject test]
调用的话会发生什么呢?不知道你有没有注意到一点,
RootMetaClass
的父类是NSObject
。所以说,在方法查找过程中,如果一个方法在RootMetaClass
上没有找到,就会继续到它的父类NSObject
上去继续找。回到上面的例子上来的话,首先会在RootMetaClass
上查找test
方法,没有找到,接着就会到NSObject
上继续找,于是就找到了我们新增的这个方法。所以说,
NSObject
上的每一个实例方法的声明,也是相当于一个类方法的声明。并且,每一个实例方法,都是可以当做类方法进行使用的,只不过对应到的实例是一个全局唯一的实例罢了。Method Forwarding
方法调用过程中,如果在类的缓存及方法列表中没有找到对应的方法,那么就会进行动态加载和转发。如果你对这一过程不是很清楚的话,可以先看一下这篇文章的消息转发部分。
对于动态加载来说,实例方法和类方法分别定义有
+resolveInstanceMethod:
和+resolveClassMethod:
来进行对应的处理。但对动态转发的两个步骤来说,NSObject
上只定义了以下几个实例方法:那么,对于类方法的动态转发过程,我们又该如何处理呢?
通过上一小节我们知道,
NSObject
上实例方法的声明和类方法的声明是等价的。所以,对于NSObject
的子类,如果需要对类方法进行动态转发,只需要实现以下方法,并在合适的地方调用super
即可:那么,如果我想重写
NSObject
上的这几个方法,来实现方法转发,我是不是只要通过Method Swizzling
替换实例的3个方法就可以了呢?这就取决于
NSObject
本身到底是只实现了实例的方法,还是实际上也实现了类方法,只是没有显式声明。验证方法很简单,我们先替换3个实例方法,然后看看类方法的转发会不会进来即可。当然,我们也可以直接阅读这部分的开源代码进行验证。不管你通过哪种方法,都可以以下结论:
NSObject
上几乎所有方法都实现了实例方法和类方法,只不过很多没有进行显式声明。所以,我们的认知又可以进一步。
NSObject
的类方法虽然可以是对应的实例方法,但NSObject
自身并没有这么去做。从源代码来看,很多实例方法和对应的类方法实现其实是完全一致的,但代码还是写了两份。这样可以让逻辑更清楚,实例方法和类方法各自有独立的继承链和根节点,不会有任何混合的可能性。在我们自己写代码的时候,也不推荐利用这层隐式的关联。结论
虽然因为
NSObject
和RootMetaClass
之间的关系,导致了NSObject
的类方法可以是对应的实例方法,但这一点并不推荐在实际开发中进行应用。需要注意的是,这一特点导致了NSObject
中的很多类方法并没有进行显式声明,实际开发中大家可以根据实际情况进行试验。