深刻淺出 Runtime(一):初識
深刻淺出 Runtime(二):數據結構
深刻淺出 Runtime(三):消息機制
深刻淺出 Runtime(四):super 的本質
深刻淺出 Runtime(五):相關面試題html
咱們先來看兩個數據結構objc_super
和objc_super2
。面試
它們的區別在於第二個成員:markdown
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對象)
};
複製代碼
再來看兩個函數objc_msgSendSuper()
和objc_msgSendSuper2()
。數據結構
從源碼來看,兩個函數所接收的參數沒有區別。函數
可是從官方註釋咱們能夠推測,objc_msgSendSuper2()
函數所接收的第一個參數應該爲objc_super2
而非objc_super
。oop
// 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, ...)
複製代碼
(id)self
和(SEL)_cmd
;objc_msgSend()
函數的調用,經過上一篇文章能夠知道,該函數會從當前消息接收者類
中開始查找方法的實現。objc_msgSendSuper2()
函數的調用,該函數會從當前消息接受者類的父類
中開始查找方法的實現。咱們經過 clang 將如下 OC 代碼 轉換爲 C++ 代碼:post
[super viewDidLoad];
複製代碼
// 轉換爲 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
轉換爲objc_msgSendSuper()
函數的調用,參數爲objc_super
和SEL
。ui
那麼爲何前面說super
會轉換爲objc_msgSendSuper2()
函數的調用呢?spa
由於轉成的 C++ 的實現和真正的底層實現是有差別的,命令行
LLVM
編譯器會將 「 OC 代碼」
先轉成 「中間代碼(.ll)」
再轉成 「彙編、機器代碼」
,該中間代碼非 C/C++。
可使用如下命令行指令生成中間代碼:clang -emit-llvm -S main.m
具體能夠查看官方文檔 LLVM,這裏不作過多介紹。
將 ViewController.m 文件轉換成彙編代碼進行驗證:
查看第 18 行代碼即[super viewDidLoad]
轉換成的彙編代碼
以上能夠看到,[super viewDidLoad]
底層其實是轉換成了objc_msgSendSuper2()
函數的調用而非objc_msgSendSuper()
。
當使用 super 調用方法的時候,底層會轉換爲objc_msgSendSuper2()
函數的調用,該函數接收兩個參數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()
函數內部會經過current_class
的superclass
指針拿到它的父類,從父類開始查找方法的實現。忽略「從 receiverClass 中查找方法的過程」,對應下圖就是直接從第 5 步開始。
要注意receiver
消息接收者仍是子類對象,而不是父類對象,只是查找方法實現的範圍變了。
@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
方法的實如今 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
開始查找方法的實現,若是沒有重寫的狀況,則會一直找到基類 NSObject,而後調用。
[super class]
是從receiverClass->superclass
開始查找方法的實現,若是沒有重寫的狀況,則會一直找到基類 NSObject,而後調用。
因爲receiver
相同,因此它們的返回值是同樣的。