究一下super
关键字,在讲之前我们先看一下下面四条语句输出打印什么
***********************?MJPerson.h ?**************************
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject
- (void)run;
@end
***********************?MJPerson.m ?**************************
#import "MJPerson.h"
@implementation MJPerson
- (void)run
{
NSLog(@"%s", __func__);
}
@end
***********************?MJStudent.h ?**************************
#import "MJPerson.h"
@interface MJStudent : MJPerson
@end
***********************?MJStudent.m ?**************************
#import "MJStudent.h"
#import <objc/runtime.h>
@implementation MJStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]); // MJStudent
NSLog(@"[self superclass] = %@", [self superclass]); // MJPerson
NSLog(@"--------------------------------");
NSLog(@"[super class] = %@", [super class]); // MJStudent
NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
}
return self;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJStudent *student = [[MJStudent alloc] init];
}
return 0;
}
RUN>
*********************** ?运行结果? ************************** 2021-05-07 16:03:26.690440+0800 Interview05-super[4285:175492] [self class] = MJStudent 2021-05-07 16:03:26.690907+0800 Interview05-super[4285:175492] [self superclass] = MJPerson 2021-05-07 16:03:26.690949+0800 Interview05-super[4285:175492] -------------------------------- 2021-05-07 16:03:26.690973+0800 Interview05-super[4285:175492] [super class] = MJStudent 2021-05-07 16:03:26.690995+0800 Interview05-super[4285:175492] [super superclass] = MJPerson
我们看到
[super class]
和[super superclass]
的打印结果不是我们所预期的。怎么回事呢?
要搞清楚这个问题我们就需要搞懂super
关键字,class()
,superClass()
的底层,我们把Student.m
转为c++
代码看看底层是怎样的:
***********************?MJStudent.h ?**************************
#import "MJPerson.h"
@interface MJStudent : MJPerson
- (void)run;
@end
***********************?MJStudent.m ?**************************
#import "MJStudent.h"
#import <objc/runtime.h>
@implementation MJStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]); // MJStudent
NSLog(@"[self superclass] = %@", [self superclass]); // MJPerson
NSLog(@"--------------------------------");
NSLog(@"[super class] = %@", [super class]); // MJStudent
NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
}
return self;
}
- (void)run
{
[super run];
NSLog(@"MJStudet.......");
}
@end
转化为底层C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 MJStudent.m -o MJStudent-arm64.cpp
以看到run
方法的底层实现如下
static void _I_MJStudent_run(MJStudent * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("MJStudent"))}, sel_registerName("run"));
//[NSLog ]
NSLog((NSString *)&__NSConstantStringImpl__var_folders_yh_qjzhl57s63j2m9l4frv27zjc0000gn_T_MJStudent_c2a994_mi_0);
}
简化一下
//
static void _I_MJStudent_run(MJStudent * self, SEL _cmd) {
//???[super run];
objc_msgSendSuper((__rw_objc_super){
(id)self,
(id)class_getSuperclass(objc_getClass("MJStudent"))
},
@selector(run));
}
*************♥️♥️♥️再精简一下
static void _I_MJStudent_run(MJStudent * self, SEL _cmd) {
//⚠️⚠️⚠️结构体单独抽出来
struct __rw_objc_super arg = {
(id)self,
(id)class_getSuperclass(objc_getClass("MJStudent"))
};
//???[super run];
objc_msgSendSuper(arg, @selector(run));
}
发现super
底层被转换为objc_msgSendSuper(arg1,arg2)
函数,里面传入连个参数__rw_objc_super 结构体和 SEL
@selector(run)
就是一个方法选择器SEL
那么__rw_objc_super
是什么呢,我们在runtime
源码中搜搜objc_super
:
//???objc源码中的定义???
/// 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
id receiver;
—— 消息接受者,其实实参传递的就是self
,也就是CLStudent
的实例对象Class super_class;
—— 父类,通过中间码里面该结构体初始化的赋值代码
(id)class_getSuperclass(objc_getClass("MJStudent")
可以看出,这个父类就是MJStudent
的父类类对象[MJPerson class]
.
objc_super
的底层就是消息的接受者和他的父类,结合这些底层知识我们把[super run]
底层的c++
代码修改一下:
struct objc_super arg = {self, [MJPerson class]};
objc_msgSendSuper(arg, @selector(run));
<font color='red'>super 方法的接受者仍然是子类,传入的父类是干嘛用的呢?</font>
接着我们在看一下objc_msgSendSuper
里面究竟干了什么事情,我们在runtime
源码中搜索objc_msgSendSuper
:
/**
* 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);
#endif
super
—— 是一个指向结构体指针struct objc_super *
,它里面的内容是{消息接受者 recv
, 消息接受者的父类类对象 [[recv superclass] class]
},objc_msgSendSuper
会将消息接受者的父类类对象
作为消息查找的起点。
原来 superclass 是为了告诉 runtime ,查找方法的时候直接从 superclass 的类中查找.❎不要在像以前那样通过 实例对象 isa 找到类对象,从类对象的方法列表中查找,如果找不到再通过类对象的 superclass 找到父类从父类的方法列表中查找.❎
[super message]的底层实现
1.消息接收者仍然是子类对象
2.从父类开始查找方法的实现
- 为了加深理解,我们对比一下给对象正常发送消息后的查找流程
[obj message]
->
在obj的类对象cls查找方法->
在cls的父类对象[cls superclass]查找方法->
在更上层的父类对象查找方法-> **...** ->
在根类类对象 NSObject里查找方法`
[super message]
->->在obj的类对象cls查找方法(跳过此步骤)
(直接从这一步开始)在cls的父类对象[cls superclass]查找方法
->在更上层的父类对象查找方法
-> ... -> `在根类类对象 NSObject里查找方法
-
class
方法的底层:
//class 底层实现
- (Class)class{
return object_getClass(self);//获取方法接受者的类对象或者元类对象
// object_getClass底层调用的 getIsa(),如果是实例对象获取的就是类对象,如果是类对象获取的就是元类对象.
}
-
superClass
方法的底层:
// superclass 底层实现
- (Class)superclass{
//先获取方法接受者的类对象
//在获取它的父类对象
return class_getSuperclass(object_getClass(self));
}
NSLog(@"[self class] = %@", [self class]); // MJStudent
- 消息接受者:
MJStudent
的实例对象- 最终调用的方法:基类
NSObject
的-(Class)class
方法2021-05-07 16:49:58.626838+0800 Interview05-super[4726:201151] [self class] = MJStudent
NSLog(@"[self superclass] = %@", [self superclass]); // MJPerson
- 消息接受者:仍然是
MJStudent
的实例对象- 最终调用的方法:基类
NSObject
的-(Class)superclass
方法
NSLog(@"[super class] = %@", [super class]); // MJStudent
- 消息接受者:
MJStudent
的实例对象- 最终调用的方法:基类
NSObject
的-(Class)class
方法
NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
- 消息接受者:仍然是
MJStudent
的实例对象- 最终调用的方法:基类
NSObject
的-(Class)superclass
方法
我们再回头看看[super class]
,[super superclass]
:
-
[super class]
:方法的接受者仍然是self
,class()
方法内部获取到self
的类对象,所以还是Student
. -
[super superclass]
:方法的接受者仍然是self
,superclass()
方法内部会现获取self
的类对象Student
,在获取Student
的父类Person
.
特别备注
本系列文章总结自MJ老师在腾讯课堂iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化,相关图片素材均取自课程中的课件。如有侵权,请联系我删除,谢谢!