OC經(jīng)典面試題class_getInstanceMethod谭胚、class_getClassMethod徐块、isKindOfClass

oc中方法存在于類中未玻,類方法存在于元類中

class_getInstanceMethod

//cls 類
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)

04F436F8-EB5C-4797-934C-31589535E469.png

意思是這個(gè)class_getInstanceMethod會(huì)返回類的實(shí)例方法

問題1

LGPerson類

@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end

@implementation LGPerson
- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}
@end

main.m

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    //獲取類名
    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));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        lgInstanceMethod_classToMetaclass(pClass);
        NSLog(@"Hello, World!");
    }
    return 0;
}

問:輸出情況?

  1. sayHello是實(shí)例方法胡控,sayHappy是類方法扳剿。
  2. pClass是類,metaClass是元類
    根據(jù)原則oc中實(shí)例方法存在與類中昼激,類方法存在與元類中
    大致可以推斷出:
    sayHello是實(shí)例方法在LGPerson類中舞终,所以
  • method1:打印出sayHello的內(nèi)存地址,
  • method2:NULL
    sayHappy是累方法癣猾,在LGPerson的元類中
  • method3:輸出是0x00即NULL
  • method4:輸出是sayHappy的內(nèi)存地址

class_getClassMethod

image.png

問題2

void lgClassMethod_classToMetaclass(Class pClass){
    
    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));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

問:輸出情況敛劝?

  1. sayHello是實(shí)例方法,sayHappy是類方法纷宇。
  2. pClass是類夸盟,metaClass是元類
    根據(jù)原則oc中實(shí)例方法存在與類中,類方法存在與元類中
    輸出情況
  • method1:0x00 即nil
  • method2: 0x00 即nil
  • method3: 0x100008148
  • method4: 0x100008148
    sayHello是實(shí)例方法像捶,所以class_getClassMethod不論怎么查詢都是nil
    sayHappy是類方法上陕,類方法存在于元類中,但是method3和method4都打印了地址拓春,释簿, 這是為什么呢?
    可以查看class_getClassMethod源碼
//獲取類方法
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();
}
//獲取isa
inline Class 
objc_object::ISA() 
{
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

通過查看源碼發(fā)現(xiàn)
class_getClassMethod本質(zhì)還是調(diào)用class_getInstanceMethod庶溶,在傳入的第一個(gè)參數(shù)的時(shí)候傳入的是Class的元類cls->getMeta()
所以method3自然就能打印出來
在getMeta()會(huì)判斷當(dāng)前的class是否為元類懂鸵,如果是就返回本身偏螺。
所以method4自身也能打印出來。

isKindOfClass和isMemberOfClass

isKindOfClass分別有類方法和實(shí)例方法

  • isKindOfClass:是給定類的實(shí)例還是從該類繼承的任何類的實(shí)例
  • isMemberOfClass:是否給定類的實(shí)例
    問:

BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);


BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson 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);

+isKindOfClass 和 - isKindOfClass的區(qū)別匆光,源碼

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

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

通過源碼結(jié)構(gòu)isa的走位圖


image.png

類方法是獲取當(dāng)前類的元類套像,不斷獲取元類父類,與傳入的類型進(jìn)行比較
實(shí)例方法是獲取當(dāng)前的類终息,不斷獲取父類夺巩,與傳入的類型進(jìn)行比較。

實(shí)際代碼運(yùn)行并沒有調(diào)用上述代碼周崭,而是通過llvm優(yōu)化后調(diào)用如下方法:

BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    //obj如果是實(shí)例對(duì)象柳譬, obj->getIsa()獲取的是類信息
    //obj如果是類對(duì)象, obj->getIsa()獲取的是元類信息
    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);
}

id:typedef struct objc_object *id; id是任意類型的objc_object
Class:class是objc_class類型休傍,而objc_class是繼承objc_object的征绎。

struct objc_class : objc_object {}
typedef struct objc_class *Class;

所以objc_opt_isKindOfClass的第一個(gè)參數(shù)obj

  • obj如果是實(shí)例對(duì)象, obj->getIsa()獲取的是類class
  • obj如果是類對(duì)象, obj->getIsa()獲取的是元類MetaClass

解析1:

 BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 

objc_opt_isKindOfClass的源碼分析
傳入的參數(shù)是[NSObject class]人柿,通過obj->getIsa()獲取到是NSObject的根元類柴墩,所以在第一次for循環(huán)的時(shí)候,cls是根元類與otherClass根類想比較凫岖,則第一次比較不相等江咳。
第二次for循環(huán)cls獲取其父類,根據(jù)isa的走位哥放,根元類的父類是NSObject類歼指,所以第二次循環(huán)相等。

   Class cls = obj->getIsa();//根元類甥雕,根元類的父類是NSObject
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }

所以輸出YES

解析2

BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];    

isMemberOfClass是判斷一個(gè)對(duì)象是否給定類的實(shí)例
所以返回NO

解析3

BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];     

objc_opt_isKindOfClass在根據(jù)其源碼和isa走位圖踩身。
傳入的obj是LGPerson的類class,經(jīng)過obj->getIsa()獲取LGPerson元類metaclass社露,而otherclass是LGPerson的類class挟阻,metaclass與class比較所以第一次循環(huán)不相等。metaclass獲取其父類峭弟,因此與LGPerson的類class相比不相等附鸽。所以不斷循環(huán)后不相等
所以返回NO

解析4

BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];    

返回NO

解析5-7

objc_opt_isKindOfClass傳入的第一個(gè)參數(shù)是個(gè)對(duì)象,經(jīng)過objc->getIsa()獲取的是類class瞒瘸,class與otherclass相比較是相等坷备。
返回yes

解析6-8

返回yes

2020-09-30 17:50:06.470167+0800 KCObjc[80323:9507443]  re1 :1
 re2 :0
 re3 :0
 re4 :0
2020-09-30 17:50:06.471930+0800 KCObjc[80323:9507443]  re5 :1
 re6 :1
 re7 :1
 re8 :1
最后編輯于
?著作權(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)離奇詭異惯雳,居然都是意外死亡朝巫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門石景,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劈猿,“玉大人,你說我怎么就攤上這事潮孽【救伲” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵往史,是天一觀的道長(zhǎng)仗颈。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么挨决? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任请祖,我火速辦了婚禮,結(jié)果婚禮上脖祈,老公的妹妹穿的比我還像新娘肆捕。我一直安慰自己,他們只是感情好盖高,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布慎陵。 她就那樣靜靜地躺著,像睡著了一般喻奥。 火紅的嫁衣襯著肌膚如雪席纽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天撞蚕,我揣著相機(jī)與錄音胆筒,去河邊找鬼。 笑死诈豌,一個(gè)胖子當(dāng)著我的面吹牛仆救,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矫渔,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼彤蔽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了庙洼?” 一聲冷哼從身側(cè)響起顿痪,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎油够,沒想到半個(gè)月后蚁袭,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一发乔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雪猪,春花似錦栏尚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至官觅,卻和暖如春纵菌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背休涤。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工咱圆, 沒想到剛下飛機(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