究一下super
關(guān)鍵字,在講之前我們先看一下下面四條語句輸出打印什么
***********************??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>
*********************** ??運行結(jié)果?? ************************** 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
image-20210507160642347我們看到
[super class]
和[super superclass]
的打印結(jié)果不是我們所預(yù)期的。怎么回事呢晌坤?
要搞清楚這個問題我們就需要搞懂super
關(guān)鍵字,class()
,superClass()
的底層,我們把Student.m
轉(zhuǎn)為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
轉(zhuǎn)化為底層C++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 MJStudent.m -o MJStudent-arm64.cpp
以看到run
方法的底層實現(xiàn)如下
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) {
//??????結(jié)構(gòu)體單獨抽出來
struct __rw_objc_super arg = {
(id)self,
(id)class_getSuperclass(objc_getClass("MJStudent"))
};
//??????[super run];
objc_msgSendSuper(arg, @selector(run));
}
發(fā)現(xiàn)super
底層被轉(zhuǎn)換為objc_msgSendSuper(arg1,arg2)
函數(shù),里面?zhèn)魅脒B個參數(shù)__rw_objc_super 結(jié)構(gòu)體和 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;
—— 父類,通過中間碼里面該結(jié)構(gòu)體初始化的賦值代碼
(id)class_getSuperclass(objc_getClass("MJStudent")
可以看出铃肯,這個父類就是MJStudent
的父類類對象[MJPerson class]
.
objc_super
的底層就是消息的接受者和他的父類,結(jié)合這些底層知識我們把[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
—— 是一個指向結(jié)構(gòu)體指針struct objc_super *
患亿,它里面的內(nèi)容是{消息接受者 recv
, 消息接受者的父類類對象 [[recv superclass] class]
}押逼,objc_msgSendSuper
會將消息接受者的父類類對象
作為消息查找的起點步藕。
原來 superclass 是為了告訴 runtime ,查找方法的時候直接從 superclass 的類中查找.?不要在像以前那樣通過 實例對象 isa 找到類對象,從類對象的方法列表中查找,如果找不到再通過類對象的 superclass 找到父類從父類的方法列表中查找.?
[super message]的底層實現(xiàn)
1.消息接收者仍然是子類對象
2.從父類開始查找方法的實現(xiàn)
- 為了加深理解,我們對比一下給對象正常發(fā)送消息后的查找流程
[obj message]
->
在obj的類對象cls查找方法->
在cls的父類對象[cls superclass]查找方法->
在更上層的父類對象查找方法-> **...** ->
在根類類對象 NSObject里查找方法`
[super message]
->->在obj的類對象cls查找方法(跳過此步驟)
(直接從這一步開始)在cls的父類對象[cls superclass]查找方法
->在更上層的父類對象查找方法
-> ... -> `在根類類對象 NSObject里查找方法
-
class
方法的底層:
//class 底層實現(xiàn)
- (Class)class{
return object_getClass(self);//獲取方法接受者的類對象或者元類對象
// object_getClass底層調(diào)用的 getIsa(),如果是實例對象獲取的就是類對象,如果是類對象獲取的就是元類對象.
}
-
superClass
方法的底層:
// superclass 底層實現(xiàn)
- (Class)superclass{
//先獲取方法接受者的類對象
//在獲取它的父類對象
return class_getSuperclass(object_getClass(self));
}
NSLog(@"[self class] = %@", [self class]); // MJStudent
- 消息接受者:
MJStudent
的實例對象- 最終調(diào)用的方法:基類
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
的實例對象- 最終調(diào)用的方法:基類
NSObject
的-(Class)superclass
方法
NSLog(@"[super class] = %@", [super class]); // MJStudent
- 消息接受者:
MJStudent
的實例對象- 最終調(diào)用的方法:基類
NSObject
的-(Class)class
方法
NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
- 消息接受者:仍然是
MJStudent
的實例對象- 最終調(diào)用的方法:基類
NSObject
的-(Class)superclass
方法
我們再回頭看看[super class]
,[super superclass]
:
-
[super class]
:方法的接受者仍然是self
,class()
方法內(nèi)部獲取到self
的類對象,所以還是Student
. -
[super superclass]
:方法的接受者仍然是self
,superclass()
方法內(nèi)部會現(xiàn)獲取self
的類對象Student
,在獲取Student
的父類Person
.
特別備注
本系列文章總結(jié)自MJ老師在騰訊課堂iOS底層原理班(下)/OC對象/關(guān)聯(lián)對象/多線程/內(nèi)存管理/性能優(yōu)化挑格,相關(guān)圖片素材均取自課程中的課件咙冗。如有侵權(quán),請聯(lián)系我刪除漂彤,謝謝雾消!