[OC]self與super調(diào)用本質(zhì)分析

一、self 基本說明

在日常開發(fā)中,我們經(jīng)常使用到self關鍵字,比如,訪問屬性,調(diào)用實例方法等。那么self到底指的是什么?我們來看一下官方文檔的解釋和定義:

Returns the receiver. //返回接收器

在看一下NSObject中關于self的一些說法:

Rather than using [[XYZPerson alloc] init] in the class factory method, instead try using [[self alloc] init].

Using self in a class factory method means that you’re referring to the class itself.

在工廠方法中使用self意味著引用類本身,意思就是說self就是當前類,那么再來看一下源碼中對self的定義,代碼(1-1),如下:

+ (id)self {
    return (id)self;
}
//-----------------------------分割線--------------------------
- (id)self {
    return self;
}

可以看到self只是返回本身,也就是當前調(diào)用者。\color{red}{(PS:調(diào)用者并不一定是當前類,繼續(xù)看下文可明白)}

因為self是消息機制中的第一個隱藏參數(shù),在消息傳遞的過程中,本賦值為調(diào)用者,也就是消息接收者。第二個隱藏參數(shù)為_cmd,也就是SEL,調(diào)用的方法。

二、super 基本說明

super關鍵字,\color{red}{(PS:不知為何大部分文章中都說super不是關鍵字,關鍵字的概念就是保留字,很明顯super是保留字)}也是我們經(jīng)??吹胶褪褂玫?。代碼(2-1),如下:

- (void)viewDidLoad {
    [super viewDidLoad];
}
//-----------------------------分割線--------------------------
- (instancetype)init {
    self = [super init];
    if(self){
        //...
    }
    return self;
}

關于super的說明,本人并沒有在官方文檔中看到,如果大家有發(fā)現(xiàn)請關注本文末尾的公眾號,告知本人,將不勝感激。只是在NSObject中有部分說明如下:

There’s another important keyword available to you in Objective-C, called super. Sending a message to super is a way to call through to a method implementation defined by a superclass further up the inheritance chain. The most common use of super is when overriding a method.

意思是說,super是一個重要的關鍵字\color{red}{(這里也有說super是關鍵字,所以網(wǎng)上很多說法是錯誤的,大家要自己甄別!)},向super發(fā)送消息是調(diào)用繼承鏈上的超類方法。super最常見的用法就是覆蓋一個方法。

三、探究selfsuper

首先創(chuàng)建一個基于 Single View App的模板項目,添加PersonStudent類,Person類繼承于NSObject類,Student類繼承于Person類,以備后面使用。下面我們在項目默認創(chuàng)建的ViewController.m類中添加代碼(3-1),如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"self:%@", NSStringFromClass([self class]));
    NSLog(@"super:%@", NSStringFromClass([super class]));
}

查看輸出結果如下:

demo1[2534:11854005] self:ViewController
demo1[2534:11854005] super:ViewController

我們發(fā)現(xiàn)selfsuper的輸出同樣是ViewController,也就是當前類。然后我們繼續(xù)試驗,在PersonStudent類中分別添加eat方法,代碼(3-2),如下:

#import "Person.h"

@implementation Person

- (void)eat {
    NSLog(@"%@ eat!", self);
}

@end
//-----------------------------分割線--------------------------
#import "Student.h"

@implementation Student

- (void)eat {
    [super eat];
    NSLog(@"%@ eat!", self);
}

@end

然后在ViewController.mViewDidLoad方法中,創(chuàng)建Student類并調(diào)用eat方法,代碼(3-3),如下:

Student * student = [[Student alloc] init];
[student eat];

運行,輸出結果如下:

demo1[2962:11906123] <Student: 0x600003fc8100> eat!
demo1[2962:11906123] <Student: 0x600003fc8100> eat!

我們明明是在Student類中通過super調(diào)用的父類Person中的eat方法,為何Person中的eat方法輸出的也是Student eat呢?關于這一部分的解釋,網(wǎng)上有很多說明解釋,總感覺不夠詳細徹底,下面我們來分析一下。

我們通過命令行將Student.m文件轉(zhuǎn)成.cpp文件,,cd進入到Student.m文件所在的目錄,使用命令如下:

xcrun -sdk iphonesimulator clang -rewrite-objc Student.m

會在當前文件夾中生成Student.cpp文件,打開找到以下代碼(3-4),

static void _I_Student_eat(Student * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("eat"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_9d3dmfcj49598v7_hghf3qjc0000gn_T_Student_370c84_mi_0, self);
}

找到關于super調(diào)用的部分代碼(3-5),如下:

((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("eat"));

我們發(fā)現(xiàn),當super調(diào)用時,其實是調(diào)用的objc_msgSendSuper方法進行的消息發(fā)送,我們看下官方文檔對于此方法的解釋:

Sends a message with a simple return value to the superclass of an instance of a class.

向超類發(fā)送消息。

通過代碼(3-5),可以得知,我們這里有兩個參數(shù),一個是結構體指針類型__rw_objc_super,一個是返回SEL類型方法編號的sel_registerName。而__rw_objc_super在我們編譯的Student.cpp文件中可以找到原型,代碼(3-6),如下:

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) {} 
};

結合官方文檔對objc_msgSendSuper方法參數(shù)的說明我們得知,__rw_objc_super,也就是objc_super。官方文檔對此參數(shù)的說明如下:

A pointer to an 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.

指向objc_super的結構體指針,傳遞標識了上下文的消息,包含了消息接收者和要在超類中搜索的方法實現(xiàn)。我們再來看一下objc_super結構體的源碼(3-7),如下:

/// 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

結合我們編譯的代碼(3-5),我們得知,消息的接收者在這里傳遞的就是當前對象self,即Student類的實例對象,方法即eat 。

通過上面的分析我們得知,在我們使用super方法調(diào)用父類方法時,其實傳遞了隱藏參數(shù)。我們上面說過,self調(diào)用返回方法本身,并不一定是你看到的當前調(diào)用者。這里可以說明,當我們在父類中使用self時,因為是在子類中通過super方法調(diào)用的父類方法,在這過程中,通過隱藏結構體參數(shù),消息接收者傳遞的是當前的self,也就是Student的實例,所以我們在父類中使用self時,其實是使用子類的實例在調(diào)用父類的方法,所以也就有了代碼(3-1)和(3-3)的輸出。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容