之前对super的理解就是简单一股脑当做self来看就行,自己也没有更深刻的认识,也不知道为什么可以简单当做self来看,这里自己测试整理一下
测试用例如下
@interface Parent : NSObject
@end
@interface son : Parent
- (void)say;
@end
@implementation Parent
@end
@implementation son
- (void)say {
NSLog(@“------------------------------");
NSLog(@"[self class]--%@",[self class]);
NSLog(@"[super class]--%@",[super class]);
NSLog(@"[super superclass]--%@",[super superclass]);
NSLog(@"[self superclass]--%@",[self superclass]);
NSLog(@“------------------------------");
}
先看输出结果
[self class]--son
[super class]--son
[super superclass]--Parent
[self superclass]—Parent
可以看到,确实将super直接当做self来理解就行,那为啥可以这么简单粗暴呢
这个得从消息发送和消息查找说起
上面四个消息调用转成底层一点的代码简单表示如下
objc_msg_send(self,@selector(say))
objc_msg_sendSuper(self,@selector(say))
objc_msg_sendSuper(self,@selector(say))
objc_msg_send(self,@selector(say))
再看objc_msg_send()
与objc_msg_sendSuper()
在objc4-723版本中全局搜索objc_msgSendSuper
,可以看到message.h
文件中有如下定义
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
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_msg_send
这个好理解,俩参数,第一个是消息接受者,第二个是个SEL
,而objc_msg_sendSuper
的第一个参数是一个objc_super
的结构体,第二个也是个SEL
,进一步查看objc_super
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
通过源码可以发现,objc_super
结构体内有俩成员,一个就是消息接受者,在上述两个调用对象为super
的情况下,其实内部真正的消息接收者还是self
,这就能解释为啥super
可以简单理解为self
的原因,看注释可以知道,该结构体作为第一个参数,其实是指定该类(即消息接受者)的父类,也就是说在查找方法的时候的查找位置,如果是self
,那就直接从该类的缓存/方法列表查找,如果是super
,那就直接从该类的父类的缓存/方法列表查找
按我个人浅薄理解,上述两个api看成如下情况即可
objc_msgSend(self,self,SEL
)
objc_msgSendSuper(self,superclass,SEL)
第一个参数是消息接收者,第二个参数是方法查找起始位置(类),第三个参数为SEL,至于苹果的做法,会是仅仅为了增加判断而已吗?看源码是这样,具体的原因,碍于自己水平太low,无法确定。。。
以上便是关于self
与super
的区别,再来分析一下实际发送消息class
和superclass
因为上述测试用例并没有实现class
和superclass
方法,所以按照正常消息发送过程来查找的话,son
类与parent
类的缓存以及方法列表都是没结果的,最终会到NSObject
当中去查找,而NSObject
是有这两个方法的实现的,具体实现如下
- (Class *)class {
return object_getClass(self);
}
- (Class)superclass {
return class_getSuperclass(object_getClass(self));
}
同样查看源码实现
Class object_getClass(id obj){
if (obj) return obj->getIsa();
else return Nil;
}
Class class_getSuperclass(Class cls){
if (!cls) return nil;
return cls->superclass;
}
结论
用self和super调用方法区别在于
1.查找方法的起始位置不同
2.调用方法的真正接收者相同
基于以上结论,如果将测试用例中的son类的父类改成NSObject,结果应该是
son son NSObject NSObject
实际测试如下
[self class]--son
[super class]--son
[super superclass]--NSObject
[self superclass]—NSObject