[self class] & [super class]
思考
请问以下代码的运行结果是什么?
@interface MYObject : NSObject
@end
@implementation MYObject
- (instancetype)init {
self = [super init];
if (self) {
NSLog(@"%@, %@", self.class, super.class);
}
return self;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MYObject *obj = [[MYObject alloc] init];
}
return 0;
}
以上代码将会在控制台输出:
MYObject, MYObject
分析
上述代码为什么打印结果和我们想象中的 MYObject, NSObject
不一样呢?想要弄清楚到底发生了什么,那我们就得从底层源码来分析。
class 方法做了什么?
通过 objc 源码可以找到 class 的实现代码如下:
- (Class)class {
return object_getClass(self);
}
通过分析源码得知,通过 class
-> object_getClass(self)
-> getIsa()
-> ISA()
这整个执行流程后,已经通过 self 的 isa 指针拿到了当前的类;这就是 class 方法的实现,那么现在的关键在于,class 中的参数 self 到底是谁???
通过 clang 查看编译后的 C++ 代码
将上面的 OC 代码编译成 C++ 代码:
clang -rewrite-objc main.m -o main.cpp
打开 main.cpp 文件并搜索定位到 init 方法,代码如下:
static instancetype _I_MYObject_init(MYObject * self, SEL _cmd) {
self = ((MYObject *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MYObject"))}, sel_registerName("init"));
if (self) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_t2_c8bf6cwj3cz1h771pr1h7nzc0000gn_T_main_404811_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")), ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MYObject"))}, sel_registerName("class")));
}
return self;
}
其中 NSLog 的这行代码就是我们的关键所在了,不过这么一大串看起来既不好看也怪吓人的,将其精简一下后,大致等同于如下:
NSLog(
"%@, %@",
objc_msgSend(self, sel_registerName("class")),
objc_msgSendSuper(
(__rw_objc_super){self, class_getSuperclass(objc_getClass("MYObject"))},
sel_registerName("class")
)
);
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) {}
};
通过上面精简后的 C++ 代码,我们知道 [super class] 其实调用了 objc_msgSendSuper
函数。
objc_msgSendSuper
方法声明如下:
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);
其中第一个参数为 objc_super
结构体的指针,该结构体声明如下(已简化):
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
/* super_class is the first class to search */
};
所以实际上 __rw_objc_super 结构体就等价于 objc_super 结构体,第一个属性就是消息接收者,第二个属性为父类。
所以当我们调用 [super class] 时,底层调用的是 objc_msgSendSuper 方法,并把 self 作为消息接收者,所以打印的还是当前的 MYObject。
objc_msgSendSuper2
上面经过编译后的 C++ 代码我们明显看到调用了 objc_msgSendSuper 函数,那么真实的调用是怎样的呢?
在 NSLog 这行代码打个断点,然后查看汇编代码:
通过汇编代码我们看到,调用的却是 objc_msgSendSuper2 函数,What happened?
继续从 objc 源码中查找蛛丝马迹,找到 objc_msgSendSuper 的实现代码:
恍然大悟,原来 objc_msgSendSuper 的实现就是调起了 objc_msgSendSuper2 函数。
// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id _Nullable
objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.6, 2.0, 9.0, 1.0, 2.0);
如上所示, objc_msgSendSuper2 函数的声明和 objc_msgSendSuper 的声明都是一样的。
但是为何上面断点中的汇编代码,直接能跳转到 objc_msgSendSuper2 而不是通过 objc_msgSendSuper 进行中转调用呢?这其中就是 LLVM 做的优化了,感兴趣可以自己深入研究下。
所以当我们手动调用 objc_msgSendSuper 函数时,实际上调用的还是 objc_msgSendSuper2,这也就和上面的汇编代码流程对上了。
objc_msgSendSuper2 就是一个过渡产物,由于 objc 版本的不断更新,这个过程中产生了 objc_msgSendSuper2 这个产物,但是需要兼容以前的版本,所以 objc_msgSendSuper 还是会保留下来,但是内部会调起 objc_msgSendSuper2;对其我们开发者来说不需要知道太多,继续保持调用 objc_msgSendSuper 就行了。
总结
[super class]
等价于[self class]
,适用于未重写 class 方法的情况self
其实是函数的形参,而super
则是编译器关键字,由编译器进行特殊处理- 如果想获取父类,请通过
[self superclass]
获取