Objective-C 中 self 与 super 的区别

以下代码的打印结果是什么?为什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
@interface JQApple : JQFruit
@end

@implementation JQApple
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end

按照面向对象的思想应该是分别打印JQAppleJQFruit
然而运行结果却出乎我们的意料,最终均都打印“JQApple”。这是为什么?

因为self是类的隐藏参数,指向当前调用方法的对象。而super并不是如我们所想是指向当前对象父类的指针。其实super是一个编译器标识符,在运行时中与self相同,指向同一个消息接受者,只是self会优先在当前类的methodLists中查找方法,而super则是优先从父类中查找。验证如下:
在终端运行:

1
$ clang -rewrite-objc main.m

可以看到运行时代码如下:

1
2
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_03322m393b5cyvhz2zhv2c100000gn_T_main_97554f_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_03322m393b5cyvhz2zhv2c100000gn_T_main_97554f_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}, sel_registerName("class"))));

删除相关无关类型及方法后代码如下:

1
2
objc_msgSend((id)self, sel_registerName("class"));
objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}, sel_registerName("class"));

查看函数定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/** 
* Sends a message with a simple return value to an instance of a class.
*
* @param self A pointer to the instance of the class that is to receive the message.
* @param op The selector of the method that handles the message.
* @param ...
* A variable argument list containing the arguments to the method.
*
* @return The return value of the method.
*
* @note When it encounters a method call, the compiler generates a call to one of the
* functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
* Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper;
* other messages are sent using \c objc_msgSend. Methods that have data structures as return values
* are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
*/
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
/**
* 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 objc_msgSendSuper(struct objc_super *super, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);

可知objc_msgSend函数中的self参数是指指向接收消息的类的实例的指针,即消息接受者,而op参数则是指处理该消息的selectorobjc_msgSendSuper函数中的参数super则是一个objc_super结构体,objc_super结构体定义如下:

1
2
3
4
5
6
7
8
9
10
/// Specifies the superclass of an instance. 
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;

/// Specifies the particular superclass of the instance to message.
__unsafe_unretained Class super_class;

/* super_class is the first class to search */
};

其中receiver是指类的实例,super_class则是指该实例的父类。可以看到在编译后的C++代码中有个__rw_objc_super结构体:

1
2
3
4
5
struct __rw_objc_super { 
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};

其实即为objc_super结构体。通过(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}该段代码可知:我们把self以及JQApple的父类通过结构体的构造方法构造了一个__rw_objc_super结构体,也就是objc_super。因此objc_super结构体中的receiver既是self。所以[self class][super class]指向的是同一个消息接受者,只是self会优先从当前类的实现中寻找方法处理消息,而super则是会优先从objc_super结构体中的super_class也就是父类的实现中查找。JQFruitJQApple中均未实现- (Class)class;方法,因此会逐级向上查找最终调用基类NSObject- (Class)class;方法,通过官方开源的NSObject- (Class)class;方法代码:

1
2
3
- (Class)class{
return object_getClass(self);
}

可知,消息接受者是self,而[self class][super class]指向的是同一个消息接受者,因此该段代码均打印JQApple