跟大家分享一道关于super和superclass的面试题,也是比较常见的面试题,请看下面的代码:
请问当前的4个结果输出是多少?我们平时写代码估计也不会这么写,面试官问你这些问题,主要是看你对super和superclass是否理解透彻,接下来我们就去分析一下.
首先我们肯定是知道[self class]这个肯定就是当前类,也就是GDStudent;而[self superclass]肯定是它的父类GDPerson
这两个应该是毫无疑问,我们看一下是不是这样,请看下图代码:
这两个是非常明显的答案,我们就来看一下super是怎么回事,这样,我们先来随便在GDStudent写一个方法比如 -(void)test;
.m文件加上实现 如下图:
是不是我们只要搞清楚test里面是怎么调用的,我们就知道super到底做了什么事!所以老样子,我们把GDStudent.m转成底层的c++文件看一下具体的实现是什么样的.只要搞清楚了[super test]做了什么事,其他的问题就迎刃而解了(转成c++的代码我前面说了很多次: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc GDStudent.m -o GDStudent.m-arm64.cpp)
所以我们只要关注绿色框框的代码就行了,我们把上面的代码简化一下,这样看得更清楚.
上面的就是源码的简化版本,是一摸一样的效果,这里的[super test]就是arg给@selector(test)发一个class_getSuperclass消息,接下来,我们就看一下这个_objc_super的源码里面是怎么实现的(注意这里是去找objc的苹果开源源码,不是刚刚转换的c++的源码,下载地址之前分享了好多次,我现在下载的是787版本),看看结构体里面到底做了什么事!如下图
__rw_objc_super源码实现
__rw_objc_super我们再可以简化一下我们离答案越来越近了,请看简化后的如下图:
这里应该也是非常的清晰吧 self:就是上面的receiver;class_getSuperclass(objc_getClass("GDStudent") :就是上面的 super_class.
接下来我们看最后一个objc_msgSendSuper这个的源码实现,就能解决这个问题了,请看下面的源码:
这里面解释的非常清楚:2个参数,第一个参数就是消息接收者,receiver也就是self,前面的结构体定义很清楚知道;第二个就是用来开始查找方法开始地方.
说白了,调用方法[super run]在开始找run方法的时候,首先是从父类的类对象(GDPerson)开始找!消息接收者还是self(GDStudent)这个结论应该很清楚吧;
结论
调用super方法的时候,是首先从父类的类对象开始查找方法,消息接收者还是当前类.
好了,知道这个结果,我们继续去看面试题,大家猜一下这两个结果是啥?
// NSLog(@"%@",[super class]);
// NSLog(@"%@",[super superclass]);
我们先来看一下结果:
结果运行
是不是有很诧异的同学.首先class方法在哪?对于一个oc对象,都有class方法这个是吧,所以class方法在NSObject里面,这里应该是没有什么问题.
现在我们去objc源码再去看一下class和superclass的底层实现,请看下图:
所以到这里我们很清楚,为什么会出现上面的结果:
NSLog(@"%@",[self class]);
NSLog(@"%@",[self superclass]);
NSLog(@"%@",[super class]);
NSLog(@"%@",[super superclass]);
分别是:当前类、当前类的父类、当前类、当前类的父类.
总结[super message]
1.消息接收者仍然是子类对象
2.从父类开始查找方法的实现.
接下来博客我会介绍isKindOfClass和isMemberOfClass区别.(小插曲)
比如你知道下面的结果值吗?
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class]isMemberOfClass:[NSObject class]];
BOOL res3 = [[GDStudent class]isKindOfClass:[GDStudent class]];
BOOL res4 = [[GDStudent class]isMemberOfClass:[GDStudent class]];