iOS中的 isKindOfClass 和 isMemberOfClass

weixin_34007886發表於2018-10-08

先來看一個爛大街的面試題:

下面程式碼結果如何?

BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[TestObject class] isKindOfClass:[TestObject class]];
BOOL res4 = [(id)[TestObject class] isMemberOfClass:[TestObject class]];

答案:除了第一個是YES,其他三個都是NO。

在推測結果之前,首先要明白兩個問題。isKindOfClassisMemberOfClass 的區別是什麼?

isKindOfClass:
returns YES if the receiver is an instance of the specified class or an instance of any class that inherits from the specified class.
方法呼叫者是傳入的類的例項物件,或者呼叫者是傳入類的繼承者鏈中的類的例項物件,則返回YES。

isMemberOfClass:
returns YES if the receiver is an instance of the specified class.
方法呼叫者必須是傳入的類的例項物件才返回YES。

我們從Runtime原始碼的角度來分析一下結果。

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->super_class) {
        if(tcls == cls) return YES;
    }
    return NO;
}
-(BOOL)isKindOfClass:(Class)cls {
    for(Class tcls = [self class]; tcls; tcls = tcls->super_class) {
        if(tcls == cls) return YES;
    }
    return NO;
}

平時開發過程中只會接觸到物件方法的isKindOfClassisMemberOfClass,但是在NSObject類中還隱式的實現了類方法版本。不只這兩個方法,其他NSObject中的物件方法,都有其對應的類方法版本。因為在OC中,類和元類也都是物件。這四個呼叫由於都是類物件發起呼叫的,所以最終執行的都是類方法版本。

先把Runtime的物件模型拿出來,方便後面的分析。

2177502-5fef06d181aa6146.png

因為呼叫類的class方法,會把類自身直接返回,所以第一次呼叫者是NSObject類物件,+ (BOOL)isKindOfClass:(Class)cls方法的引數cls也是NSObject類物件。

進入到for迴圈中,在此說明一下object_getClass()方法相當於取出isa指標指向的類,所以會從NSObject的元類開始遍歷,所以第一次NSObject meta class != NSObject class,匹配失敗。第二次迴圈將tcls 設定為superclassNSObject classNSObject class==NSObject class,匹配成功。

NSObject 能匹配成功,是因為這個類比較特殊,在第二次獲取 superClass的時候,NSObject 元類的superClass就是NSObject的類物件,所以會匹配成功。而其他三種匹配,則都會失敗,各位同學可以去自己分析一下剩下三種。

參考資料

部落格地址 作者:劉小壯

相關文章