OC-Runtime-super關(guān)鍵字

image-20210507155714545

究一下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:

image-20210507162852053
//??????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)系我刪除漂彤,謝謝雾消!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挫望,隨后出現(xiàn)的幾起案子立润,更是在濱河造成了極大的恐慌,老刑警劉巖媳板,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桑腮,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛉幸,警方通過查閱死者的電腦和手機破讨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門旨巷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人添忘,你說我怎么就攤上這事∪羲” “怎么了搁骑?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長又固。 經(jīng)常有香客問我仲器,道長,這世上最難降的妖魔是什么仰冠? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任乏冀,我火速辦了婚禮,結(jié)果婚禮上洋只,老公的妹妹穿的比我還像新娘辆沦。我一直安慰自己,他們只是感情好识虚,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布肢扯。 她就那樣靜靜地躺著,像睡著了一般担锤。 火紅的嫁衣襯著肌膚如雪蔚晨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天肛循,我揣著相機與錄音铭腕,去河邊找鬼。 笑死多糠,一個胖子當(dāng)著我的面吹牛累舷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播熬丧,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笋粟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了析蝴?” 一聲冷哼從身側(cè)響起害捕,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闷畸,沒想到半個月后尝盼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡佑菩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年盾沫,在試婚紗的時候發(fā)現(xiàn)自己被綠了裁赠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赴精,死狀恐怖佩捞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蕾哟,我是刑警寧澤一忱,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站谭确,受9級特大地震影響帘营,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逐哈,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一芬迄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昂秃,春花似錦禀梳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哗戈,卻和暖如春郊艘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唯咬。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工纱注, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胆胰。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓狞贱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜀涨。 傳聞我的和親對象是個殘疾皇子瞎嬉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容