OC底層原理09 - 經(jīng)典面試題分析

面試題1

分析以下代碼分別輸出什么饰躲?

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

        BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL re6 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
        BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
        BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];

        NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
    }
    return 0;
}

isKindOfClass

類方法isKindOfClass

該方法主要是用來(lái)判斷當(dāng)前類的元類條件類是否相等嬉橙。

  • 如果相等,返回YES祷肯;
  • 如果不相等唯卖,則繼續(xù)查找該元類的父類粱玲,使之與條件類進(jìn)行比較。
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

實(shí)例方法isKindOfClass

該方法主要是用來(lái)判斷當(dāng)前對(duì)象所屬的類條件類是否相等拜轨。

  • 如果相等抽减,則返回YES;
  • 如果不相等橄碾,則繼續(xù)查找該類的父類卵沉,使之與條件類進(jìn)行比較。
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

示例1

[(id)[NSObject class] isKindOfClass:[NSObject class]]

  1. 通過(guò)[NSObject class]分析得知法牲,調(diào)用的是類方法isKindOfClass史汗。
  2. 第一次判斷
    clsNSObject類
    tclsNSObject元類拒垃;
    cls 不等于 tcls停撞。
    修改tclsNSObject元類的父類,即:NSObject類悼瓮。
  3. 第二次判斷
    clsNSObject類戈毒;
    tclsNSObject類
    cls 等于 tcls横堡。

結(jié)果:返回YES埋市。

示例2

[(id)[LGPerson class] isKindOfClass:[LGPerson class]]

  1. 通過(guò)[LGPerson class]分析得知,調(diào)用的是類方法isKindOfClass命贴。
  2. 第一次判斷
    clsLGPerson類道宅;
    tclsLGPerson元類
    cls 不等于 tcls胸蛛。
    修改tclsLGPerson元類的父類污茵,即根元類NSObject
  3. 第二次判斷
    clsLGPerson類葬项;
    tcls根元類泞当;
    cls 不等于 tcls
    修改tclsNSObject元類的父類玷室,即:NSObject類零蓉。
  4. 第三次判斷
    clsLGPerson類笤受;
    tclsNSObject類穷缤;
    cls 不等于 tcls
    修改tclsNSObject類的父類箩兽,即:nil津肛。
  5. 由于tclsnil药版,循環(huán)結(jié)束堆缘。

結(jié)果:返回NO

示例3

[(id)[NSObject alloc] isKindOfClass:[NSObject class]]

  1. 通過(guò)[NSObject alloc]分析得知,調(diào)用的是對(duì)象方法isKindOfClass十兢。
  2. 第一次判斷
    clsNSObject類
    tclsNSObject類肺缕;
    cls 等于 tcls狈涮。

結(jié)果:返回YES

示例4

[(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]

  1. 通過(guò)[LGPerson alloc]分析得知涯鲁,調(diào)用的是對(duì)象方法isKindOfClass巷查。
  2. 第一次判斷
    clsLGPerson類
    tclsLGPerson類抹腿;
    cls 等于 tcls岛请。

結(jié)果:返回YES

isMemberOfClass

類方法isMemberOfClass

該方法主要用來(lái)判斷當(dāng)前類的元類條件類是否相等警绩。

  • 如果相等崇败,則返回YES;
  • 如果不相等肩祥,則返回NO后室;
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

實(shí)例方法isMemberOfClass

該方法主要用來(lái)判斷當(dāng)前對(duì)象所屬的類條件類是否相等。

  • 如果相等搭幻,則返回YES咧擂;
  • 如果不相等,則返回NO檀蹋;
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

示例5

[(id)[NSObject class] isMemberOfClass:[NSObject class]]

  1. 通過(guò)[NSObject class]分析得知松申,調(diào)用的是類方法isMemberOfClass
  2. 當(dāng)前元類NSObject元類俯逾,條件類NSObject贸桶。

結(jié)果:不相等,返回NO桌肴。

示例6

[(id)[LGPerson class] isMemberOfClass:[LGPerson class]]

  1. 通過(guò)[LGPerson class]分析得知皇筛,調(diào)用的是類方法isMemberOfClass
  2. 當(dāng)前元類LGPerson類的元類坠七,條件類LGPerson類水醋。

結(jié)果:不相等,返回NO彪置。

示例7

[(id)[NSObject alloc] isMemberOfClass:[NSObject class]]

  1. 通過(guò)[NSObject alloc]分析得知拄踪,調(diào)用的是實(shí)例方法isMemberOfClass
  2. 當(dāng)前對(duì)象類NSObject類拳魁,條件類NSObject類惶桐。

結(jié)果:相等,返回YES

示例8

[(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]

  1. 通過(guò)[LGPerson alloc]分析得知姚糊,調(diào)用的是實(shí)例方法isMemberOfClass贿衍。
  2. 當(dāng)前對(duì)象類LGPerson類條件類LGPerson類救恨。

結(jié)果:相等贸辈,返回YES

面試題1輸出結(jié)果:

2020-09-30 14:59:08.857321+0800 KCObjc[72747:9401735]  
 re1 :1
 re2 :0
 re3 :1
 re4 :1
2020-09-30 14:59:12.976812+0800 KCObjc[72747:9401735]  
 re5 :0
 re6 :0
 re7 :1
 re8 :1
Program ended with exit code: 0

總結(jié)

  1. 類方法 isKindOfClass 和 isMemberOfClass
    相同:比較 元類 與條件類肠槽。
    不同:isKindOfClass 在第一次比較不成功之后裙椭,會(huì)繼續(xù)查找元類的父類,直至找到nil署浩,而 isMemberOfClass 在比較不成功之后直接返回false揉燃。
  2. 實(shí)例方法 isKindOfClass 和 isMemberOfClass
    相同:比較 類與 條件類
    不同:isKindOfClass 在第一次比較不成功之后,會(huì)繼續(xù)查找類的父類筋栋,直至找到nil炊汤,而 isMemberOfClass 在比較不成功之后直接返回false。

面試題2

創(chuàng)建一個(gè) Person 類弊攘,繼承自 NSObject抢腐,分別添加一個(gè)實(shí)例方法和一個(gè)類方法

@interface LCPerson : NSObject

- (void)sayHello;
+ (void)say666;

@end

@implementation LCPerson

- (void)sayHello{
    NSLog(@"sayHello");
}

+ (void)say666{
    NSLog(@"say666");
}

@end

分析以下代碼分別輸出什么?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
        Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

        Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
        Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
        
        NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
    }
    return 0;
}

class_getInstanceMethod

該方法主要是用于獲取實(shí)例方法襟交,如果在傳入的類或者類的父類中沒(méi)有找到指定的實(shí)例方法迈倍,則返回NULL。針對(duì)該方法捣域,蘋果有如下說(shuō)明

image.png

根據(jù)之前的分析啼染,
實(shí)例方法是存儲(chǔ)在對(duì)象所屬的類中
類方法是存儲(chǔ)元類中焕梅。

分析

  • method1
    目的:在LGPerson類中查找sayHello的實(shí)例方法
    依據(jù):實(shí)例方法是存儲(chǔ)在對(duì)象所屬的類中
    實(shí)際:在LGPerson類中有定義sayHello的實(shí)例方法
    結(jié)果:method1是存在的迹鹅。
  • method2
    目的:在LGPerson的元類中查找sayHello的實(shí)例方法
    依據(jù):實(shí)例方法是存儲(chǔ)在對(duì)象所屬的類中
    實(shí)際:sayHello的實(shí)例方法是存在LGPerson類
    結(jié)果:method2是不存在的。
  • method3
    目的:在LGPerson類中查找sayHappy的實(shí)例方法
    依據(jù):實(shí)例方法是存儲(chǔ)在對(duì)象所屬的類中
    實(shí)際:在LGPerson類中沒(méi)有定義sayHappy的實(shí)例方法
    結(jié)果:method3是不存在的贞言。
  • method4
    目的:在LGPerson的元類中查找sayHappy的實(shí)例方法
    依據(jù):實(shí)例方法是存儲(chǔ)在對(duì)象所屬的類中
    實(shí)際:在LGPerson類中沒(méi)有定義sayHappy的實(shí)例方法斜棚,但是由于LGPerson類定義了sayHappy的類方法,而類方法以實(shí)例方法的形式存儲(chǔ)在元類中该窗。
    結(jié)果:method4是存在的弟蚀。

面試題2輸出結(jié)果:

2020-09-30 15:01:41.051611+0800 KCObjc[72806:9404232] 
 method1 :0x100008100
 method2 :0x0
 method3 :0x0
 method4 :0x100008098
Program ended with exit code: 0

面試題3

此題是對(duì)面試題2的延伸,因此準(zhǔn)備條件與面試題2一樣酗失。
分析以下代碼分別輸出什么义钉?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getClassMethod(pClass, @selector(sayHello));
        Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

        Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
        Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
        
        NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
    }
    return 0;
}

class_getClassMethod

該方法主要是用于獲取類方法,一個(gè)指向方法數(shù)據(jù)結(jié)構(gòu)的指針级零,它指向類指定的類方法的實(shí)現(xiàn)断医,如果指定的類或它的父類不包含具有指定的類方法,則為NULL奏纪。針對(duì)該方法鉴嗤,蘋果有如下說(shuō)明

image.png

分析

Method class_getClassMethod(Class cls, SEL sel) {
    if (!cls  ||  !sel) return nil;
    return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
}

由源碼知道,獲取類方法其實(shí)就是獲取元類的實(shí)例方法序调。

  • method1
    傳入的參數(shù)是LGPerson類醉锅,但在class_getClassMethod中,先根據(jù)LGPerson類獲取LGPerson元類发绢,然后再找LGPerson元類中的實(shí)例方法sel硬耍。此時(shí)selsayHello方法
    LGPerson元類中只存儲(chǔ)了sayHappy 方法边酒。
    結(jié)果:method1是不存在的经柴。

  • method2
    傳入的參數(shù)是LGPerson元類,此時(shí)查找的是LGPerson元類中的實(shí)例方法sel墩朦。此時(shí)selsayHello方法坯认。
    LGPerson元類中只存儲(chǔ)了sayHappy 方法
    結(jié)果:method2是不存在的氓涣。

  • method3
    傳入的參數(shù)是LGPerson類牛哺,但在class_getClassMethod中,先根據(jù)LGPerson類獲取LGPerson元類劳吠,然后再找LGPerson元類中的實(shí)例方法sel引润。此時(shí)selsayHappy方法
    LGPerson元類中存儲(chǔ)了sayHappy 方法痒玩。
    結(jié)果:method3是存在的淳附。

  • method4
    傳入的參數(shù)是LGPerson元類,此時(shí)查找的是LGPerson元類中的實(shí)例方法sel蠢古。此時(shí)selsayHappy方法燃观。
    LGPerson元類中存儲(chǔ)了sayHappy 方法
    結(jié)果:method4是存在的便瑟。

面試題3輸出結(jié)果:

2020-09-30 15:12:42.559984+0800 KCObjc[72925:9410227] 
 method1 :0x0
 method2 :0x0
 method3 :0x100008098
 method4 :0x100008098
Program ended with exit code: 0

總結(jié)

  • 實(shí)例方法存儲(chǔ)在對(duì)象所屬的類中
  • 類方法存儲(chǔ)在元類中
  • 在元類中缆毁,類方法是以實(shí)例方法的形式存儲(chǔ)的
  • 對(duì)象是所屬類的實(shí)例
  • 類是元類的實(shí)例
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市到涂,隨后出現(xiàn)的幾起案子脊框,更是在濱河造成了極大的恐慌,老刑警劉巖践啄,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浇雹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屿讽,警方通過(guò)查閱死者的電腦和手機(jī)昭灵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門吠裆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人烂完,你說(shuō)我怎么就攤上這事试疙。” “怎么了抠蚣?”我有些...
    開(kāi)封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵祝旷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我嘶窄,道長(zhǎng)怀跛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任柄冲,我火速辦了婚禮吻谋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘现横。我一直安慰自己滨溉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布长赞。 她就那樣靜靜地躺著晦攒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪得哆。 梳的紋絲不亂的頭發(fā)上脯颜,一...
    開(kāi)封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音贩据,去河邊找鬼栋操。 笑死,一個(gè)胖子當(dāng)著我的面吹牛饱亮,可吹牛的內(nèi)容都是我干的矾芙。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼近上,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剔宪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起壹无,我...
    開(kāi)封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤葱绒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后斗锭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體地淀,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年岖是,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帮毁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实苞。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烈疚,靈堂內(nèi)的尸體忽然破棺而出黔牵,到底是詐尸還是另有隱情,我是刑警寧澤胞得,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站屹电,受9級(jí)特大地震影響阶剑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜危号,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一牧愁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧外莲,春花似錦猪半、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至声邦,卻和暖如春乏奥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背亥曹。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工邓了, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人媳瞪。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓骗炉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛇受。 傳聞我的和親對(duì)象是個(gè)殘疾皇子句葵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348