美文网首页
类相关面试题分析

类相关面试题分析

作者: 小糾丶羯 | 来源:发表于2020-09-19 22:38 被阅读0次

    一、类方法为什么存在元类中

    首先,我们在LGPerson中定义两个方法,一个实例方法,一个类方法

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

    并且在main.m中定义了几个函数:
    1、lgObjc_copyMethodList函数:获取方法列表

    void lgObjc_copyMethodList(Class pClass){
        unsigned int count = 0;
        Method *methods = class_copyMethodList(pClass, &count);
        for (unsigned int i=0; i < count; i++) {
            Method const method = methods[i];
            //获取方法名
            NSString *key = NSStringFromSelector(method_getName(method));
            
            LGLog(@"Method, name: %@", key);
        }
        free(methods);
    }
    

    2、lgInstanceMethod_classToMetaclass函数:获取实例方法

    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);
    }
    

    3、lgClassMethod_classToMetaclass函数:获取类方法

    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);
    }
    

    我们在main函数中调用上面相关函数

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            LGPerson *person = [LGPerson alloc];
            Class pClass     = object_getClass(person);
            
            lgObjc_copyMethodList(pClass);
            
            lgInstanceMethod_classToMetaclass(pClass);
            
            lgClassMethod_classToMetaclass(pClass);
        }
        return 0;
    }
    

    打印结果如下:

    Method, name: sayHello
    lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
    lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
    

    在获取实例方法中,method1:0x1000031b0method2:0x0method3:0x0method4:0x100003148,其中method1method4表示找到方法,method2method3表示未找到方法。

    我们进入lgInstanceMethod_classToMetaclass中看到,pClass是我们传入的LGPerson类metaClass是通过pClass获取到的LGPerson元类

    通过method1method2,我们可以看出实例方法存在中(LGPerson类中确实声明了一个sayHello方法),通过method3method4,我们可以看出类方法存在元类中,但是method4通过获取实例方法为什么能得到sayHappy这个类方法呢?

    我们看一下lgClassMethod_classToMetaclass,通过method3method4,我们的可以看出元类都有sayHappy这个类方法,不是说类方法存在元类中吗?怎么中也会有呢?

    这里我们来看下class_getClassMethod的源码实现:

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

    发现其中实现的还是获取实例方法,只是其中多了一个cls->getMeta(),我们继续看下其源码:

    Class getMeta() {
            if (isMetaClass()) return (Class)this;
            else return this->ISA();
        }
    

    可以看到这里有一个判断:判断传进来的是不是元类,是的话直接返回,不是的话就根据isa返回元类。这里就很好的解释了刚刚上面的两个疑问了。
    总结
    1、实例方法存在
    2、类方法存在元类
    3、获取类方法本质是获取元类实例方法

    二、isKindOfClass & isMemberOfClass

    刚刚我们探究了实例方法以及类方法,同样的这两个方法也存在实例方法类方法,那它们有什么不同呢?我们先来输出打印下:
    1、isKindOfClass & isMemberOfClass 类方法调用

    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);
    

    打印结果:

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

    首先是+ (BOOL)isKindOfClass:(Class)cls的源码:

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

    结合示例与源码我们来看一下:
    re1(NSObject):首先是获取元类tcls,所以tcls根元类,然后根元类cls(这里是NSObject类)比较,肯定是不相等的,继续循环,tcls指向它的父类,我们知道根元类父类NSObject,即tcls这时等于NSObject,满足tcls == cls,返回YES

    re3(LGPerson):先是tclsLGPerson元类LGPerson类进行比较,不相等后继续循环,根据superclass走向图,tcls最终是走向niltcls == cls永不成立,返回NO

    再来是+ (BOOL)isMemberOfClass:(Class)cls的源码:

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

    可以看到,这里直接是元类进行比较,re2(NSObject)re4(LGPerson)条件永不成立,返回NO

    2、isKindOfClass & isMemberOfClass 实例方法调用

    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);
    

    打印结果:

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

    首先是- (BOOL)isKindOfClass:(Class)cls的源码:

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

    结合示例与源码我们来看一下:
    可以看到这里是获取对象的与传入的进行比较,相等则返回YES,不等则继续循环tcls指向superclass再比较,循环结束还不成立,返回NO,这里re5(NSObject)re7(LGPerson)条件恒成立,返回YES

    再来是- (BOOL)isMemberOfClass:(Class)cls的源码:

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

    可以看到,这里只有对象的与传入的进行比较,成立返回YES,不成立返回NO,这里re6(NSObject)re8(LGPerson)条件恒成立,返回YES

    总结
    1、isKindOfClass
    类方法:元类--->根元类--->根类--->nil与传入的比较
    实例方法:---> 父类---> 根类---> nil与传入的比较

    2、isMemberOfClass
    类方法:元类与传入的比较
    实例方法:与传入的比较

    相关文章

      网友评论

          本文标题:类相关面试题分析

          本文链接:https://www.haomeiwen.com/subject/djqqyktx.html