学习MJ的视频课程,整理总结知识点–super、class面试题

本文讲解super、class相关的一些面试题,利用底层的知识来分析讲解

面试题01-super

先看一段代码,同时附上了分析时的DemoInterview05-super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface MJPerson : NSObject

@end

@interface MJStudent : MJPerson

@end

- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[self superclass] = %@", [self superclass]);

NSLog(@"[super class] = %@", [super class]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}

问:打印结果是什么?
验证后的输出结果如下

1
2
3
4
NSLog(@"[self class] = %@", [self class]);          // MJStudent
NSLog(@"[self superclass] = %@", [self superclass]);// MJPerson
NSLog(@"[super class] = %@", [super class]); // MJStudent
NSLog(@"[super superclass] = %@", [super superclass]);// MJPerson

对于[self class][self superclass]的结果我们基本上没什么疑问,对于[super class]的结果,可能会觉得意外。

下面结合Demo以及编译后的源码分析为什么是这样。

先从简答的super调用分析,代码如下

1
2
3
4
5
6
@implementation MJStudent
- (void)run {
[super run];
NSLog(@"MJStudet.......");
}
@end

利用clang编译对应部分的源码,结果如下

1
2
3
4
5
6
7
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MJStudent"))}, sel_registerName("run"));

// 代码进行格式优化后
objc_msgSendSuper((__rw_objc_super){
(id)self,
(id)class_getSuperclass(objc_getClass("MJStudent"))
}, @selector(run));

我们发现,objc_msgSendSuper的参数有两个,一个是结构体,一个是SEL,我们搜索源码发现这个结构体相关的代码

1
2
3
4
5
/// Specifies the superclass of an instance. 
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
};

objc_super的结构和__rw_objc_super是一致的,其实它们两个也是等价的,只不过是程序在不同阶段的表现不同而已。

所以,可以整理出super方法调用对应的源码

1
2
3
4
5
6
7
8
9
10
11
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
};

- (void)run {
[super run];
// 等价源码如下
// struct objc_super arg = {self, [MJPerson class]};
// objc_msgSendSuper(arg, @selector(run));
}

objc_super中的receiver消息接受者是self,即[super run]中super调用的receiver仍然是MJStudent对象。

下面的关键就是看objc_msgSendSuper的实现,但是这部分是汇编代码,不过能看到它的注释,写的也很清晰,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** 
* Sends a message with a simple return value to the superclass of an instance of a class.
*
* @param super A pointer to an \c objc_super data structure. Pass values identifying the
* context the message was sent to, including the instance of the class that is to receive the
* message and the superclass at which to start searching for the method implementation.
* @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method identified by \e op.
*
* @see objc_msgSend
*/
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

这段注释也说明[super message]的底层实现原理

  1. 消息接收者仍然是子类对象
  2. 从父类开始查找方法的实现

分析完后,继续回到[super class]这段代码

1
NSLog(@"[super class] = %@", [super class]);

结合上面的super分析,我们可知[super class]这个方法的接受者仍然是当前的子类对象(MJStudent)。

对于-class方法,结合我们在object_getClass所学的知识,它的实现如下

1
2
3
4
5
6
7
- (Class)class {
return object_getClass(self);
}

- (Class)superclass {
return [self class]->superclass;
}

所以[super class]的消息接收者是当前的子类对象,而-class方法就是返回方法调用者的class,也就是MJStudent类型

总结

  1. [super message]转化的底层源码
    struct objc_super {
    __unsafe_unretained _Nonnull id receiver; // 消息接收者
    __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
    };
    struct objc_super arg = {self, 父类};

objc_msgSendSuper(arg, SEL);

  1. [super message]的底层实现
    消息接收者仍然是子类对象
    从父类开始查找方法的实现

  2. [super message], [self message]
    这两个函数的实现差别挺大,一个调用objc_msgSendSuper,一个调用objc_msgSend,不要被它们两个表面迷惑;

  3. -class的底层实现
    返回的是消息接收者的类,而恰好super的消息接收者是子类对象,就解释了[super class]的结果是子类了。

参考和源码

源码:
Interview05-super

评论