在面試時一般都會注重基礎的,不管是對初級,中級還是高級。本人主要介紹一下在OC中最常用的兩個關鍵字self和super,它們常用在對象初始化方法里。不知道大家有沒有想過研究過這個初始化方法?
問題:
下面是對象初始化方法:
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
}
return self;
}
相信很多人對這段代碼非常熟悉,在讀完后你需要明白幾個問題。
1. self和super是什么?
2. [super init]做了什么?
2. 為什么要把[super init]賦值給self?
答案:
問題1解析:
self是一個隱藏參數變量,指向當前調用方法的對象,還有一個隱藏參數是_cmd,代表當前方法selector。在runtime時會調用objc_msgSend()方法。
super并不是隱藏參數,只是編譯器的指令符號,在runtime時調用objc_msgSendSuper()方法。
官方文檔針對self和super已有解釋。
官方文檔中self相關解釋
Whenever you’re writing a method implementation, you have access to an important hidden value, self. Conceptually, self is a way to refer to “the object that’s received this message.” It’s apointer, just like the greeting value above, and can be used to call a method on the current receiving object.
super解釋
There’s anotherimportant 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.
所以可以簡單理解:
self調用自己方法,super調用父類方法
self是類,super是預編譯指令
[self class]和[super class]輸出是一樣的
問題2解析:
想要明白這個問題需要理解self和super的底層實現原理。
Self調用方法:
- 當使用 self 調用方法時,會首先從當前類的方法列表中開始尋找,如果沒有再從父類中尋找;
- 當使用 self 調用方法時,RT時會使用 objc_msgSend 函數:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
第一個參數是消息接收者,第二個參數是調用的具體類方法的selector,后面是 selector 方法的可變參數。
- 以 [self setSize:] 為例,編譯器會轉換成objc_msgSend的函數調用,其中 theReceiver 是 self,theSelector 是 @selector(setSize:),這個 selector 是從當前 self 的 class 的方法列表開始找的 setSize,一旦找到后把對應的selector傳遞過去。
Super調用方法:
- 當使用 super 時,則從父類的方法列表中開始找,然后調用父類的這個方法。
- 當使用 super 調用時,RT時會使用 objc_msgSendSuper 函數:
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一個參數是個objc_super的結構體,第二個參數還是類似objc_msgSend類方法的selector。而objc_super的結構體如下:
struct objc_super {
id receiver;
Class superClass;
};
- 當編譯器遇到 [super setSize:] 時,開始做下面幾個事:
1)構建objc_super的結構體,此時這個結構體的第一個成員變量receiver就是子類,和 self 中相同。而第二個成員變量superClass就是指父類
調用 objc_msgSendSuper 的方法,將這個結構體和setSize的selector傳遞過去。
2)函數里面做的事情類似這樣:從objc_super結構體指向的superClass的方法列表開始找 setSize的selector,找到后再用objc_super->receiver去調用這個selector。
知道self和super的原理就會很容易明白為什么[self class]和[super class]輸出結果會是一樣的。
問題3解析:
OC具有類繼承特性,子類繼承父類從而獲得相關的屬性和方法,所以在子類的初始化方法中,必須首先調用父類的初始化方法,完成父類相關資源的初始化。
[super init]去self的super中調用init, 然后super會調用其父類的init,以此類推,直到找到根類NSObject中的init。然后根類中的init負責初始化內存區域,添加一些必要的屬性,返回內存指針,延著繼承鏈,指針從上到下進行傳遞,同時在不同的子類中可以向內存添加必要的屬性。最后直到我們當前類中把內存地址賦值給self參數。當然,如果調用[super init]失敗的話,通過判斷self來決定是否執行子類的初始化操作。
為了更清楚理解self和super,看下面兩個例子:
示例#1:
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
答案:
控制臺輸出打印結果:
Son
Son
當發送class消息時不管是用self還是用super,接受消息主體依然是self ,也就是說self和super指向同一個對象,所以都會打印出Son。
示例2#:
@interface Father : NSObject
- (void)printCurrentClass;
@end
@implementation Father
- (void)printCurrentClass {
NSLog(@"printCurrentClass:%@", [self class]);
}
@end
@interface Son : Father
- (void)printSuperClass;
@end
@implementation Son
- (void)printSuperClass {
[super printCurrentClass];
}
@end
// 調用方法
Son *son = [Son new];
[son printCurrentClass]; // 直接調用父類方法,子類沒有重載
[son printSuperClass]; // 間接調用父類方法
答案:
控制臺輸出結果:
printCurrentClass:Son
printCurrentClass:Son
printCurrentClass方法體中self始終指向方法的接收者對象son,倘若換成[super class],結果也是一樣的。