標籤:
題目
上題目,已知A是爺爺,B是爸爸,C是孫子。
@interface A : NSObject- (void)f;@end@interface B : A- (void)f;- (void)g;@end@interface C : B- (void)f;@end
A,B,C都各自實現了函數f,只有B實現了函數g。
@implementation A- (void)f{ NSLog(@"A");}@end@implementation B- (void)f{ NSLog(@"B");}- (void)g{ [self f]; [super f]; NSLog(@"%@", [self class]); NSLog(@"%@", [super class]);}@end@implementation C- (void)f{ NSLog(@"C");}@end
請問,下面代碼輸出什嗎?
C *c = [[C alloc] init];[c g];
答案是CACC.
分析
1.C中沒有g,為什麼可以調用B的方法
C繼承自B,所以C中雖然沒有g方法,但是會去從下往上找,找打B類的g方法進行調用。
2.關於[self f]
調用B的g的過程中,self其實C,因為此時函數g訊息的接受者是c。所以系統從self的類(即C)開始從下往上找,因為C中實現了f,所以直接輸出C。
3.關於[super f]
這個問題是最困惑我的,臥槽,居然輸出的是A而不是B?!
首先要明確的一點是,self和super不是同一個層面上的東西。
self是什嗎?
self 是類的隱藏的參數,指向當前當前調用方法的類,另一個隱藏參數是 _cmd,代表當前類方法的 selector。
super是什嗎?
super可不是想象中的self.superclass啊
super是Objective-C的文法糖,是一個編譯器指示符,像宏一樣,運行時XCode可不認識super這個東西。
其實Objective-C的函數調用的本質是
id objc_msgSend(id theReceiver, SEL op, ...)
省略符號表示不定參數。
即[self f]的本質是 objc_msgSend(self, f, "");
所以,這點跟C++不同,並不是self去調用了函數f,而是訊息f發送給了self,self是訊息接受者。正是因為這樣,Objective-C中[nil f]這種寫法沒問題,因為發送給nil的任何訊息都返回nil。
objc_msgSend(theReceiver, op, ...)的本質是,從theReceiver所在的類開始找,不斷往上找,看看哪個類實現了op,一旦找到則將訊息發送給它處理。
但是[super f] 可不就是這樣了,[super f]本質是
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
省略符號表示不定參數。
即[self f]的本質是 objc_msgSendSuper(super, f, "");
objc_super何許人也?
struct objc_super { __unsafe_unretained id receiver; __unsafe_unretained Class super_class;};
objc_super的receiver是什嗎?
reveiver是當前訊息的接受者,代碼裡寫的是[c g],所以super的receiver就是C.你也可以理解為就是self。
objc_super的super_class是什嗎?
super_class是當前super所在代碼的類的父類,很繞口,但是這句一定要好好理解。比如無論誰調用了g,是C也好是B也罷,g中[super f]的super的super_class一定是A,因為super這5個字母是寫在B裡面的。
objc_msgSendSuper(super, op, ...)的本質是,從super_class->super_class所在的類開始找,不斷往上找,看看哪個類實現了op,一旦找到了則將訊息發送給它處理。
在編譯的時候,編譯器看到了super,會因此構造一個struct。objc_super->receiver填了C,objc_super->super_class填了A。因此運行時編譯器變不認識了super。
如果你已經清楚了上面的邏輯,請回頭看看[super f]。
[super f],super->reveiver是C,super->super_class是A。所以從A開始找f,找到了則系統可能這樣調用
objc_msgSend(super->recevier, f, nil) //此時f是已經找到了的f,即A的f
4.關於[self class]
首先非常有必要看看class的實現,參考自蘋果RunTime開原始碼
- (Class)class { return object_getClass(self);}
可以看到class返回的是self的類名。
好,繼續,[self class]中,class也是一個訊息,接受者是self,也就是C,所以系統從C開始找class函數的實現,沒找到,找B,找A都找不到,到了NSObjcect這一層找到了。
此時調用了NSObject的class方法,那此時self是C,那就返回了C。
5.關於[super class]
這個問題一開始看起來也很奇怪,居然[self class]和[super class]輸出了同一個東西,這不科學啊。但是把上面的知識理解好了,也就不奇怪了。
執行[super class]時,編譯器將[super class]替換為
objc_msgSendSuper(super, class, nil)
其中super為reveiver=C,superclass=A。
系統從A開始找class,找不到,到了NSObject這一層找到了。
那麼系統可能這樣調用
objc_msgSend(objc_super->receiver, class, nil) //此時class是已經找到了的class,即NSObject的class
此時調用了NSObject的class方法,那此時self是C,那就返回了C。
總結
道理懂了之後就是思考的效率問題了,凡是self的就從訊息接受者的類開始從下往上找;凡是super的就從寫super的這個類開始往上找。
參考連結
《刨根問底Objective-C Runtime(1)- Self & Super》
《Objective-C 中Self 和 Super 詳解》
《蘋果RunTime開原始碼》
《Objective-C Super的理解》
Objective-C self super