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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容