super是什么晨汹?先別急著回答踱葛,看看下面這段代碼
說一下繼承關系:Person -> Animal -> NSObject
NSLog(@"super---%@", [super className]);
這行代碼的運行結(jié)果可能和預期的不太一樣,不應該是Animal
嗎炉爆?Xcode又抽了蔬浙?當然不是,讓我們重新認識一下super誊爹。
self與super
- self和_cmd是類的隱藏參數(shù)
- 誰調(diào)用self蹬刷,self就指向誰,如類方法中的self指向當前類對象频丘,對象方法中的self指向當前類的實例對象(如果父類中某個類\對象方法有self办成,子類調(diào)用父類的這個方法,此時父類中的self指向子類的類\實例)
- _cmd其實是個SEL搂漠,下面這行代碼在哪里調(diào)用迂卢,就會打印當前調(diào)用方法的方法名,作用類似于__func__
NSLog(@"%@", NSStringFromSelector(_cmd));
- super并不是父類標示符,而是編譯器的指示符冷守,怎么理解刀崖?讓我們重新回到這里
NSLog(@"super---%@", [super className]);
NSLog(@"self---%@", [self className]);
className是NSObject的屬性,而代碼中沒有重寫 -(NSString *)className
拍摇,所以兩處打印都是調(diào)用NSObject中的get方法亮钦。那么底層究竟是怎么實現(xiàn)的?
self調(diào)用時
id objc_msgSend(id self充活, SEL op蜂莉, ...)
super調(diào)用時
id objc_msgSendSuper(struct objc_super *super, SEL op混卵, ...)
淺析數(shù)據(jù)結(jié)構
為了更好的理解參數(shù)含義映穗,我們先看看id、objc_super究竟分別是什么
可以看到幕随,id本質(zhì)是結(jié)構體objc_object的指針蚁滋,其中有一個Class isa成員,通過isa我們可以找到對象所屬的類別赘淮,這也是為什么id可以表示任意對象的原因
注意:在KVO中辕录,isa在運行時會被修改,指向一個中間類梢卸,對于編譯器而言走诞,isa的指向才是最真實的類型
receiver表示某個子類的實例,super_class表示當前類的父類
底層實現(xiàn)
接著我們來看看調(diào)用[receiver message]
會做些什么
- objc_msgSend
id objc_msgSend(id self蛤高, SEL op蚣旱, ...)
receiver通過isa指針找到當前對象的class,并在class中尋找op戴陡,如果找到塞绿,調(diào)用op,如果沒找到恤批,到super_class中繼續(xù)尋找位隶,如此循環(huán)直到NSObject(后續(xù)文章會介紹如果NSObject中還是沒找到會怎樣)
不難看出[self className]
在NSObject中找到對應的方法并調(diào)用,所以返回Person
- objc_msgSendSuper
id objc_msgSendSuper(struct objc_super *super开皿, SEL op涧黄, ...)
本文中[super className]
對應到objc_super,receiver表示Person的實例赋荆,super_class表示Animal類笋妥。同樣,先在super_class查找窄潭,沒找到op再到super_class的super_class中查找春宣,在NSObject中找到op。此時內(nèi)部的調(diào)用情況是這樣的:
objc_msgSend(objc_super -> receiver, @selector(className)月帝, ...)
這里的objc_super -> receiver就是Person的實例躏惋,所以[super className]
的運行結(jié)果仍然是Person
而不是Animal
題外話
現(xiàn)在撇開Runtime回到示例代碼本身,如果讓[super className]
運行的結(jié)果是Animal
要怎么做嚷辅?
一種做法是簿姨,在Animal中重寫className的get方法:
- (NSString *)className {
return NSStringFromClass([Animal class]);
}
當然,由于在Animal中重寫了這個方法簸搞,此時Person中的
NSLog(@"super---%@", [super className]);
NSLog(@"self---%@", [self className]);
輸出均為Animal