《OC底層-經典面試1》isKindOfClass&isMemberOfClass

前言

今天我們結合前面所學的OC底層來對經典的面試題進行分析,將學習成果運用到實踐中。

目錄

目錄.png

1、簡介

在日常開發中我們經常用到isKindOfClass或者isMemberOfClass的實例方法或者類方法,其是由RunTime提供封裝的Api,但是我們是否深入理解和掌握了呢?

1.1、經典面試題

看看下面的代碼輸出什么

// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end

// Person.m
#import "Person.h"
@implementation Person
@end

// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];
        BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];
        BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
        BOOL re4 = [[Person class] isKindOfClass:[Person class]];
        NSLog(@" re1 :%d\n re2 :%d\n re3 :%d\n re4 :%d\n",re1,re2,re3,re4);

        BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
        BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];
        BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL re8 = [[Person class] isMemberOfClass:[Person class]];
        NSLog(@" re5 :%d\n re6 :%d\n re7 :%d\n re8 :%d\n",re5,re6,re7,re8);
    }
    return 0;

上面是關于isKindOfClass 和 isMemberOfClass的經典分析題目,輸出結果你答對了么?

 re1 :1
 re2 :1
 re3 :1
 re4 :0

 re5 :1
 re6 :1
 re7 :0
 re8 :0

上面的題目實際上考察了OC底層的isa、superclass走位以及isKindOfClass&isMemberOfClass的源碼如何實現,
讓我們回顧一下isa和superclass走位圖,對源碼分析的時候需要用到

isa&superclass.png

有了前面的《OC底層》的學習的知識,并結合上圖以及源碼,可以很好的分析了。

2、isKindOfClass

2.1、- (BOOL)isKindOfClass:

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

- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

根據源碼和isa走位圖可以知道,- (BOOL)isKindOfClass:用于判斷當前實例所屬的類是否==cls,不相等則沿著superclass走位一直遍歷比較到NSObject類,路徑上有相等的則返回false,遍歷結束均不相等則返回false
其對比路徑如下圖紅色部分:

image.png

2.1.1、[[NSObject alloc] isKindOfClass:[NSObject class]]
BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];

比較過程如下圖所示

re5.png

tcls初始值為[[NObject alloc] class],即[NSObject class],因此re1==1;

2.1.2、[[Person alloc] isKindOfClass:[Person class]]
BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];

re2.png

[Person alloc]的isa指向Person類,即[Person class],因此re2==1;

2.2、+ (BOOL)isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

根據源碼和isa走位圖可以知道,+ (BOOL)isKindOfClass:用于判斷當前類的元類是否==cls,不相等則從該類的元類開始,沿著superclass一直遍歷比較到NSObject類是否==cls,有相等的則返回true,遍歷結束均不相等返回false。按照如下圖標紅部分進行比較:

+ (BOOL)isKindOfClass:

2.2.1、 [[NSObject class] isKindOfClass:[NSObject class]]

BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
re3.png

tcls初始值為[NSObject class]->ISA(),即NSObject元類,NSObject元類的superclass又指向[NSObject class],因此re3==1;

2.2.2、 [[Person class] isKindOfClass:[Person class]]

BOOL re4 = [[Person class] isKindOfClass:[Person class]];

re4.png

從上圖可以看到,tcls初始值為[Person class]->ISA(),即Person元類,一直比較遍歷到NSObject類,均不等于[Person class],因此re4==0;

3、isMemberOfClass

3.1、- (BOOL)isMemberOfClass:

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

比較當前實例的類是否==cls。

3.1.1、[[NSObject alloc] isMemberOfClass:[NSObject class]]
BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];

[self class] = [[NSObject alloc] class] == [NSObject class],
cls=[NSObject class],所以 [self class] == cls == 1;

3.1.2、[[Person alloc] isMemberOfClass:[Person class]];
BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];

[self class]= [[Person alloc] class] == [Person class] == 1;
cls=[Person class],所以 [self class] == cls == 1;

3.2、+ (BOOL)isMemberOfClass:

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

比較當前類的元類是==cls

3.2.1、[[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];

根據isa走位圖,[[NSObject class]->ISA()為NSObject的元類,不等于[[NSObject class],因此re6==0;

3.2.2、[[Person class] isMemberOfClass:[Person class]];
BOOL re8 = [[Person class] isMemberOfClass:[Person class]];

根據isa走位圖,[[Person class]->ISA()為Person元類,不等于[[Person class],因此re8==0;

4、注意

實際運行源碼調試的時候會發現,isMemberOfClass會走到源碼處,但是isKindOfClass不會,isKindOfClass的示例方法和類方法實際上調用的都是** objc_opt_isKindOfClass**

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

原因在于llvm中編譯時對其進行了優化處理。

5、總結

  • - (BOOL)isKindOfClass:用于判斷當前實例所屬的類是否==cls,不相等則沿著superclass走位一直遍歷比較到NSObject類,路徑上有相等的則返回false,遍歷結束均不相等則返回false
  • + (BOOL)isKindOfClass:用于判斷當前類的元類是否==cls,不相等則從該類的元類開始,沿著superclass一直遍歷比較到NSObject類是否==cls,有相等的則返回true,遍歷結束均不相等返回false。
  • -(BOOL)isMemberOfClass:比較當前實例的類是否==cls。
  • +(BOOL)isMemberOfClass:比較當前類的元類是否==cls。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373