Runtime 系列文章
深入淺出 Runtime(一):初識
深入淺出 Runtime(二):數(shù)據(jù)結(jié)構(gòu)
深入淺出 Runtime(三):消息機(jī)制
深入淺出 Runtime(四):super 的本質(zhì)
深入淺出 Runtime(五):具體應(yīng)用
深入淺出 Runtime(六):相關(guān)面試題
目錄
- 1. objc_super 與 objc_msgSendSuper
- 2. self 和 super
self
super- 3. super 本質(zhì)
LLVM & 中間代碼
通過匯編驗(yàn)證
super 本質(zhì)- 4. 相關(guān)面試題
1. objc_super 與 objc_msgSendSuper
我們先來看兩個數(shù)據(jù)結(jié)構(gòu)objc_super
和objc_super2
尘执。
它們的區(qū)別在于第二個成員:
objc_super
:super_class // receiverClass 的父類
objc_super2
:current_class // receiverClass(消息接收者的class對象)
// message.h(objc4)
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class; // receiverClass 的父類
#endif
/* super_class is the first class to search */
};
// objc_runtime_new.h(objc4)
struct objc_super2 {
id receiver; // 消息接收者
Class current_class; // receiverClass(消息接收者的class對象)
};
再來看兩個函數(shù)objc_msgSendSuper()
和objc_msgSendSuper2()
。
從源碼來看,兩個函數(shù)所接收的參數(shù)沒有區(qū)別,
但是從官方注釋我們可以推測,objc_msgSendSuper2()
函數(shù)所接收的第一個參數(shù)應(yīng)該為objc_super2
而非objc_super
。
// message.h(objc4)
void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
// objc-abi.h(objc4)
// objc_msgSendSuper2() takes the current search class, not its superclass.
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
2. self 和 super
self
- OC 方法都帶有兩個隱式參數(shù):
(id)self
和(SEL)_cmd
; - self 是一個對象指針假颇,指向當(dāng)前方法的調(diào)用者/消息接收者;
如果是實(shí)例方法符相,它就是指向當(dāng)前類的實(shí)例對象拆融;
如果是類方法蠢琳,它就是指向當(dāng)前類的類對象。 - 當(dāng)使用 self 調(diào)用方法的時候镜豹,底層會轉(zhuǎn)換為
objc_msgSend()
函數(shù)的調(diào)用傲须,通過上一篇文章可以知道,該函數(shù)會從當(dāng)前消息接收者類
中開始查找方法的實(shí)現(xiàn)趟脂。
super
- super 是一個編譯器指令泰讽;
- 當(dāng)使用 super 調(diào)用方法的時候,底層會轉(zhuǎn)換為
objc_msgSendSuper2()
函數(shù)的調(diào)用昔期,該函數(shù)會從當(dāng)前消息接受者類的父類
中開始查找方法的實(shí)現(xiàn)已卸。
3. super 本質(zhì)
我們通過 clang 將以下 OC 代碼 轉(zhuǎn)換為 C++ 代碼:
[super viewDidLoad];
// 轉(zhuǎn)換為 C++
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
// 簡化
struct objc_super arg = {
self,
class_getSuperclass(objc_getClass("ViewController"))
};
objc_msgSendSuper(arg, sel_registerName("viewDidLoad"));
可以看到,Runtime 將super
轉(zhuǎn)換為objc_msgSendSuper()
函數(shù)的調(diào)用硼一,參數(shù)為objc_super
和SEL
累澡。
LLVM & 中間代碼
那么為什么前面說super
會轉(zhuǎn)換為objc_msgSendSuper2()
函數(shù)的調(diào)用呢?
因?yàn)檗D(zhuǎn)成的 C++ 的實(shí)現(xiàn)和真正的底層實(shí)現(xiàn)是有差異的般贼,
LLVM
編譯器會將“ OC 代碼”先轉(zhuǎn)成“中間代碼(.ll)”再轉(zhuǎn)成“匯編愧哟、機(jī)器代碼”,該中間代碼非 C/C++哼蛆。
可以使用以下命令行指令生成中間代碼:clang -emit-llvm -S main.m
具體可以查看官方文檔 LLVM蕊梧,這里不做過多介紹。
通過匯編驗(yàn)證
將 ViewController.m 文件轉(zhuǎn)換成匯編代碼進(jìn)行驗(yàn)證:
查看第 18 行代碼即
[super viewDidLoad]
轉(zhuǎn)換成的匯編代碼
以上可以看到腮介,
[super viewDidLoad]
底層實(shí)際上是轉(zhuǎn)換成了objc_msgSendSuper2()
函數(shù)的調(diào)用而非objc_msgSendSuper()
肥矢。
super 本質(zhì)
- 當(dāng)使用 super 調(diào)用方法的時候,底層會轉(zhuǎn)換為
objc_msgSendSuper2()
函數(shù)的調(diào)用叠洗,該函數(shù)接收兩個參數(shù)struct objc_super2
和SEL
甘改。
struct objc_super2 {
id receiver; // 消息接收者
Class current_class; // receiverClass
};
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
-
objc_msgSendSuper2()
函數(shù)內(nèi)部會通過current_class
的superclass
指針拿到它的父類,從父類開始查找方法的實(shí)現(xiàn)惕味。忽略“從 receiverClass 中查找方法的過程”楼誓,對應(yīng)下圖就是直接從第 5 步開始。
- 要注意
receiver
消息接收者還是子類對象名挥,而不是父類對象,只是查找方法實(shí)現(xiàn)的范圍變了主守。
4. 相關(guān)面試題
Q:調(diào)用以下 init 方法的打印結(jié)果是什么禀倔?
@interface HTPerson : NSObject
@end
@interface HTStudent : HTPerson
@end
@implementation HTStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@",[self class]);
NSLog(@"[super class] = %@",[super class]);
NSLog(@"[self superclass] = %@",[self superclass]);
NSLog(@"[super superclass] = %@",[super superclass]);
}
return self;
}
@end
[self class] = HTStudent
[super class] = HTStudent
[self superclass] = HTPerson
[super superclass] = HTPerson
class
和superclass
方法的實(shí)現(xiàn)在 NSObject 類中,可以看到它們的返回值取決于receiver
参淫。
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
[self class]
是從receiverClass
開始查找方法的實(shí)現(xiàn)救湖,如果沒有重寫的情況,則會一直找到基類 NSObject涎才,然后調(diào)用鞋既。
[super class]
是從receiverClass->superclass
開始查找方法的實(shí)現(xiàn)力九,如果沒有重寫的情況,則會一直找到基類 NSObject邑闺,然后調(diào)用跌前。
由于receiver
相同,所以它們的返回值是一樣的陡舅。
下一篇
未完待續(xù)抵乓。。靶衍。