1、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
super is a flag that tells the compiler to search for the method implementation in a very different place. It begins in the superclass of the class that defines the method where super appears.
結(jié)論:
self : 是一個(gè)隱私參數(shù),熟悉C的都知道猪狈,他和 _cmd構(gòu)成方法的參數(shù),self是動(dòng)態(tài)的;執(zhí)行時(shí)調(diào)用RT的objc_msgSend();
super : 是個(gè)編譯器的指令符號,只是告訴編譯器在執(zhí)行的時(shí)候,去調(diào)誰的方法.super是編譯的桥状;執(zhí)行時(shí)調(diào)用RT的objc_msgSendSuper();
self 調(diào)用的是本類的方法茂缚,super調(diào)用的是父類中的方法戏罢。
2、self 和 super的底層實(shí)現(xiàn)原理:
將下面的代碼clang后阱佛,我們來看一下這兩句代碼到底是怎樣轉(zhuǎn)化并執(zhí)行的:
- (void)test{
[self class];
[super class];
}
編譯后的代碼:
static void _I_Man_test(Man * self, SEL _cmd) {
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));//[self class]調(diào)用
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Man"))}, sel_registerName("class"));//[super class]調(diào)用
}
經(jīng)過clang后帖汞,編譯為上面的代碼,簡化一下可以看到:[self class]實(shí)際上轉(zhuǎn)換成為objc_msgSend()來發(fā)送消息凑术。 [super class]實(shí)際上轉(zhuǎn)換為objc_msgSendSuper()來發(fā)送消息翩蘸。
分別看下objc_msgSend()和objc_msgSendSuper()的定義。
1)objc_msgSend()函數(shù)聲明如下:
id objc_msgSend(id theReceiver, SEL theSelector, ...)
theReceiver:消息接受者淮逊,theSelector:要調(diào)用的方法催首,后面為可變參數(shù)。其中self就是這里的theReceiver參數(shù)泄鹏。
2)objc_msgSendSuper()函數(shù)聲明如下:
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
第一個(gè)參數(shù)是是一個(gè)objc_super 結(jié)構(gòu)體郎任,不再是一個(gè)id類型的對象,后面的參數(shù)跟objc_msgSend相同备籽,分別是selector和可選參數(shù)舶治。
3) 我們再來看一下struct objc_super定義:
/// 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__ //這里采用宏判斷,現(xiàn)在是OBJC2.0,所以不會(huì)走這個(gè)if车猬。
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
};
我們可以看到結(jié)構(gòu)體主要是由reviceiver和super_class構(gòu)成的霉猛。而receiver就是一個(gè)類的實(shí)例對象(也就是self),super_class則是指向父類珠闰。即:首先從super->super_class指向的父類的方法中查找對應(yīng)的selector惜浅,找到后再使用 super->receiver 調(diào)用對應(yīng)的selector。
了解了底層實(shí)現(xiàn)后伏嗜,我們也就清楚為什么 [self class]
和 [super class]
返回值是相同的坛悉。
再來看一個(gè)例子:
@implementation Person
- (void)personBaseMethod {
NSLog(@"%@",[self class]);
}
@end
@implementation Student
- (void)personBaseMethod {
NSLog(@"%@",[self class]);
NSLog(@"========");
[super personBaseMethod];
}
//調(diào)用 Student中的personBaseMethod 打印結(jié)果如下:
Student
========
Student
self
指向的是當(dāng)前Student
的實(shí)例對象,而super則是:先去父類的方法列表中找到personBaseMethod
伐厌,然后給self
(也就是Student
的實(shí)例對象)發(fā)消息。由self對象來執(zhí)行父類personBaseMethod
這個(gè)方法體中的[self class]
裸影。 所以最終還是由self來執(zhí)行sel_registerName("class")
這個(gè)selector
挣轨。
關(guān)于objc_msgSend
函數(shù)的實(shí)現(xiàn)我們是看不到的,它的實(shí)現(xiàn)是以匯編語言完成的空民。這里有一篇博客刃唐,作者根據(jù)機(jī)器碼反推出了實(shí)現(xiàn)。
2界轩、為什么要有 self = [super init];
init方法在初始化失敗后會(huì)返回nil画饥,OC中關(guān)于 init 的約定有一個(gè)重要部分:可以通過返回 nil 來告訴調(diào)用者,初始化失敗了浊猾;(初始化可能會(huì)因?yàn)楦鞣N原因失敗抖甘,比如一個(gè)輸入的格式錯(cuò)誤了,或者另一個(gè)需要的對象初始化失敗了葫慎。)
這樣我們就能理解為什么總是需要調(diào)用 self = [super init]衔彻。
原因就是:1、如果父類說初始化自己的時(shí)候失敗了偷办,那么必須假定當(dāng)前的self實(shí)例正處于一個(gè)不穩(wěn)定的狀態(tài)艰额,因此在你的實(shí)現(xiàn)里不要繼續(xù)你自己的初始化并且也返回 nil。如果不這樣做椒涯,你可能會(huì)操作一個(gè)不可用的對象柄沮,它的行為是不可預(yù)測的,最終可能會(huì)導(dǎo)致你的程序崩潰废岂。
2祖搓、alloc后返回的是一個(gè)有效但初始化的對象,init負(fù)責(zé)初始化對象湖苞,拯欧,使對象處于可用狀態(tài)。所以沿著繼承鏈往上調(diào)用init犯法财骨,使得對象的各級父類都能夠完成初始化操作镐作,并為其實(shí)例變量賦予合理的值。
3隆箩、關(guān)于OC的alloc 和 init(摘自:OC禪與藝術(shù) )
我們常常寫 [[NSObject alloc] init] 這樣的代碼滑肉,從而淡化了 alloc 和 init 的區(qū)別。Objective-C 的這個(gè)特性叫做 兩步創(chuàng)建
摘仅。
這意味著申請分配內(nèi)存和初始化被分離成兩步,alloc 和 init问畅。
1娃属、 alloc 負(fù)責(zé)創(chuàng)建對象六荒,這個(gè)過程包括分配足夠的內(nèi)存來保存對象,寫入 isa 指針矾端,初始化引用計(jì)數(shù)掏击,以及重置所有實(shí)例變量。
2秩铆、init 負(fù)責(zé)初始化對象砚亭,這意味著使對象處于可用狀態(tài)。這通常意味著為對象的實(shí)例變量賦予合理有用的值殴玛。
alloc 方法將返回一個(gè)有效的未初始化的對象實(shí)例捅膘。每一個(gè)對這個(gè)實(shí)例發(fā)送的消息會(huì)被轉(zhuǎn)換成一次 objc_msgSend() 函數(shù)的調(diào)用,形參 self 的實(shí)參是 alloc 返回的指針滚粟;這樣 self 在所有方法的作用域內(nèi)都能夠被訪問寻仗。
按照慣例,為了完成兩步創(chuàng)建凡壤,新創(chuàng)建的實(shí)例第一個(gè)被調(diào)用的方法將是 init 方法署尤。注意,NSObject 在實(shí)現(xiàn) init 時(shí)亚侠,只是簡單的返回了 self曹体。