前面兩篇文章分別記錄了本身學習 Runtime 的一些知識點以及常見的一些應用。以前立下 flag 說準備寫三篇關於 Runtime 的文章,因而就有了這篇文章。面試
本文準備利用前面學習的內容來解答兩道在sunnyxx的神經病院objc runtime入院考試的面試題。bash
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
複製代碼
結果: Son / Sonapp
分析:學習
對於上面的答案,第一個的結果應該是咱們的預期結果,可是第二個結果卻讓咱們很費解了。ui
那咱們利用前面文章講過的知識點來分析一下整個的流程。spa
由於,Son 及 Father 都沒有實現 -(Class)calss 方法,因此這裏全部的調用最終都會找到基類 NSObject 中,而且在其中找到 -(Class)calss 方法。那咱們須要瞭解的就是在 NSObject 中這個方法的實現了。指針
在 NSObject.mm 中能夠找到 -(Class)class 的實現:code
- (Class)class {
return object_getClass(self);
}
複製代碼
在 objc_class.mm 中找到 object_getClass 的實現:對象
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
複製代碼
ps:上面的方法定義能夠去官方OpenSource中下載源碼哦。blog
能夠看到,最終這個方法返回的是,調用這個方法的 objc 的 isa 指針。那咱們只須要知道在題幹中的代碼裏面最終是誰在調用 -(Class)class 方法就能夠找到答案了。
接下來,咱們利用 clang -rewrite-objc 命令,將題乾的代碼轉化爲以下代碼:
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Car"))}, sel_registerName("class"))));
複製代碼
從上方能夠得出,調用 [Father class] 的時候,本質是在調用
objc_msgSendSuper(struct objc_super *super, SEL op, ...)
複製代碼
struct objc_super 的定義以下:
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__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
複製代碼
從定義能夠得知:當利用 super 調用方法時,只要編譯器看到super這個標誌,就會讓當前對象去調用父類方法,本質仍是當前對象在調用,是去父類找實現,super 僅僅是一個編譯指示器。可是消息的接收者 receiver 依然是self。最終在 NSObject 獲取 isa 指針的時候,獲取到的依舊是 self 的 isa,因此,咱們獲得的結果是:Son。
擴展一下: 看看下方的代碼會輸出什麼?
@interface Father : NSObject
@end
@implementation Father
- (Class)class {
return [Father class];
}
@end
---
@interface Son : Father
@end
@implementation Son
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
int main(int argc, const char * argv[]) {
Son *foo = [[Son alloc]init];
return 0;
}
---輸出:---
Father
Father
複製代碼
@interface Sark : NSObject
@end
@implementation Sark
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"%@", [NSObject class]);
NSLog(@"%@", [Sark class]);
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
NSLog(@"%d--%d--%d--%d", res1, res2, res3, res4);
}
return 0;
}
複製代碼
結果: 1--0--0--0
分析:
首先,咱們先去查看一下題幹中兩個方法的源碼:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
複製代碼
能夠得知:
[NSObject class] 執行完以後調用 isKindOfClass,第一次判斷先判斷 NSObject 和 NSObject 的 meta class 是否相等,以前講到 meta class 的時候放了一張很詳細的圖,從圖上咱們也能夠看出,NSObject 的 meta class 與自己不等。接着第二次循環判斷 NSObject 與meta class 的 superclass 是否相等。仍是從那張圖上面咱們能夠看到:Root class(meta) 的 superclass 就是 Root class(class),也就是 NSObject 自己。因此第二次循環相等,因而第一行 res1 輸出應該爲YES。
isa 指向 NSObject 的 Meta Class,因此和 NSObject Class不相等。
[Sark class] 執行完以後調用 isKindOfClass,第一次 for 循環,Sark 的 Meta Class 與 [Sark class] 不等,第二次 for 循環,Sark Meta Class 的 super class 指向的是 NSObject Meta Class, 和 Sark Class 不相等。第三次 for 循環,NSObject Meta Class 的 super class 指向的是 NSObject Class,和 Sark Class 不相等。第四次循環,NSObject Class 的super class 指向 nil, 和 Sark Class 不相等。第四次循環以後,退出循環,因此第三行的 res3 輸出爲 NO。
isa 指向 Sark 的 Meta Class,和 Sark Class 也不等。