iOS中isa面试相关知识解析

isa走位图

前言

之前我们学习了类的相关知识和isa走位,为了加深印象,接下来我们通过两个例子来复习一下,这两个例子也是一下大厂可能出现的面试题

一、isKindOfClass和isMemberOfClass

void testISATachnology(){
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL re3 = [(id)[WJPerson class] isKindOfClass:[WJPerson class]];
    BOOL re4 = [(id)[WJPerson class] isMemberOfClass:[WJPerson 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)[WJPerson alloc] isKindOfClass:[WJPerson class]];
    BOOL re8 = [(id)[WJPerson alloc] isMemberOfClass:[WJPerson class]];
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}

大家知道上面这段代码打印的结果是什么吗?
我们先来分析下题目,从上面代码中可以看到前4行代码调用的都是类方法,后4行都是对象方法。
我们先来分析一下+ (BOOL)isKindOfClass:(Class)cls类方法,我们看下苹果的源码实现

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

从上述代码中可以看出先找到本身的元类所以tcls为当前类的元类,然后用tclstcls的父类对比,如果一样则返回YES
我们先来分析下res1的结果

  • tclsNSObject的元类也就是根元类clsNSObject也就是根类
    得出结论tcls != cls,继续往下走,tcls根元类的父类也就是根类,此时tcls == cls,返回为YES

我们再来分析下res3的结果

  • tclsWJPerson的元类,clsWJPerson
    得出结论tcls != cls,继续往下走,tclsWJPerson元类的父类,也就是根元类
    得出结论tcls != cls,继续往下走,tcls根元类的父类也就是根类
    得出结论tcls != cls,继续往下走,tcls根类的父类为nil
    得出结论tcls != cls,此时跳出循环,返回NO

通过上面的分析我们可以得出结论:如果指定类是当前类的元类的父类则返回YES,否则返回NO

我们再来分析一下- (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,否则返回NO

从题目中得知re5re7中都是当前对象的类==指定类所以都返回为YES
接下来我们分析一下+ (BOOL)isMemberOfClass:(Class)cls这个类方法

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

从上述代码中可以得出结论:如果指定类是当前类的元类,则返回YES,否则返回NO

从题目中得知re2re4中都是指定类==当前类,所以都返回为NO
最后我们分析一下- (BOOL)isMemberOfClass:(Class)cls这个对象方法

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

从上述代码中可以得出结论:如果当前对象的类是指定类则返回YES,否则返回NO

从题目中得知re6re8中都是当前对象的类==指定类,所以都返回为YES
虽有我们可以推导出打印结果为 1 0 0 0 1 1 1 1
我们看下实际打印结果

2020-09-15 14:59:39.598314+0800 KCObjc[2299:91882]  re1 :1
 re2 :0
 re3 :0
 re4 :0
2020-09-15 14:59:39.601873+0800 KCObjc[2299:91882]  re5 :1
 re6 :1
 re7 :1
 re8 :1
总结
  • + (BOOL)isKindOfClass:(Class)cls:如果cls是当前类的元类的父类则返回YES,否则返回NO。
  • - (BOOL)isKindOfClass:(Class)cls:如果当前对象的类是cls或其子类则返回YES,否则返回NO。
  • + (BOOL)isMemberOfClass:(Class)cls:如果cls是当前类的元类,则返回YES,否则返回NO。
  • - (BOOL)isMemberOfClass:(Class)cls:如果当前对象的类是cls则返回YES,否则返回NO。

二、类的方法查找

@interface WJPerson : NSObject

- (void)sayHello;

+ (void)sayGoodbye;

@end

@implementation WJPerson

- (void)sayHello{
    NSLog(@"%s",__func__);
}

+ (void)sayGoodbye{
    NSLog(@"%s",__func__);
}

@end

我们先在类中定义一个对象方法,一个类方法。然后判断下面几段代码的打印结果。

一:class_getInstanceMethod探索

void instanceMethod_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(sayGoodbye));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayGoodbye));
    
    NSLog(@"%s - %d-%d-%d-%d",__func__,method1!=nil,method2!=nil,method3!=nil,method4!=nil);
}

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

我们先看下class_getInstanceMethod的源码实现

/***********************************************************************
* class_getInstanceMethod.  Return the instance method for the
* specified class and selector.
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    // This deliberately avoids +initialize because it historically did so.

    // This implementation is a bit weird because it's the only place that 
    // wants a Method instead of an IMP.

#warning fixme build and search caches
        
    // Search method lists, try method resolver, etc.
    lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);

#warning fixme build and search caches

    return _class_getMethod(cls, sel);
}

由方法的注释可以知道class_getInstanceMethod的作用是获取指定类和指定sel的实例方法
这里pClassWJPerson类,meteClassWJPerson的元类。
通过iOS底层之类结构分析这篇文章我们得知类中存放的是类的实例方法元类中存放的是类的类方法,注:类的类方法也可以叫做元类的实例方法。所以我们可以得出结论method1method4不为空,其它为空,所以推导出打印结果为 1 0 0 1
看下实际打印结果

instanceMethod_classToMetaclass - 1-0-0-1

二、class_getClassMethod探索

void classMethod_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(sayGoodbye));
    //
    Method method4 = class_getClassMethod(metaClass, @selector(sayGoodbye));
    
    NSLog(@"%s - %d-%d-%d-%d",__func__,method1!=nil,method2!=nil,method3!=nil,method4!=nil);
}

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

我们先看下class_getClassMethod的源码实现

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;
    /**
     * // NOT identical to this->ISA when this is a metaclass
     * Class getMeta() {
     *     if (isMetaClass()) return (Class)this;
     *     else return this->ISA();
     * }
     * 通过查看getMeta()实现发现,如果当前cls为元类则返回自身
     */
    return class_getInstanceMethod(cls->getMeta(), sel);
}

由方法的注释可以知道class_getClassMethod的作用是获取指定类和指定sel的类方法。但是进一步观察发现实际上是获取指定类的元类和指定sel的实例方法.
这里pClassWJPerson类,meteClassWJPerson的元类。

  • method1是获取WJPerson的元类的sayHello方法,而sayHello为是WJPerson类的实例方法,所以获取不到,method1为nil,同理method2也为nil
  • method3是获取WJPerson的元类的sayGoodbye方法,sayGoodbyeWJPerson的元类的实例方法,所以method3不为nil,同理method4也不为nil
    所以我们可以得出结论method3method4不为空,其它为空,所以推导出打印结果为 0 0 1 1
    看下实际打印结果
classMethod_classToMetaclass - 0-0-1-1

三、class_getMethodImplementation探索

void iMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayGoodbye));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayGoodbye));

    NSLog(@"%s - %d-%d-%d-%d",__func__,imp1!=nil,imp2!=nil,imp3!=nil,imp4!=nil);
}

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

我们先看下class_getMethodImplementation的源码实现

/** 
 * Returns the function pointer that would be called if a 
 * particular message were sent to an instance of a class.
 * 
 * @param cls The class you want to inspect.
 * @param name A selector.
 * 
 * @return The function pointer that would be called if \c [object name] were called
 *  with an instance of the class, or \c NULL if \e cls is \c Nil.
 *
 * @note \c class_getMethodImplementation may be faster than \c method_getImplementation(class_getInstanceMethod(cls, name)).
 * @note The function pointer returned may be a function internal to the runtime instead of
 *  an actual method implementation. For example, if instances of the class do not respond to
 *  the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
 */
IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    // 查找方法的实现
    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    // 如果没有找到则进入消息转发流程
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

由方法的注释可以知道class_getMethodImplementation的作用是获取指定类的指定方法实现
这里pClassWJPerson类,meteClassWJPerson的元类。

  • imp1是获取WJPerson的类的sayHello方法实现,sayHello为是WJPerson类的实例方法,且已经进行了实现,所以可以获取到,imp1为-[WJPerson sayHello]
  • imp2是获取WJPerson的元类的sayHello方法实现,而sayHello为是WJPerson类的实例方法,虽然进行了实现,但并不能在WJPerson的元类中查找到实现,这是会进入消息转发流程,返回的是libobjc.A.dylib _objc_msgForward
  • imp3是获取WJPerson类的sayGoodbye方法,sayGoodbyeWJPerson的元类的实例方法,虽然进行了实现,但并不能在WJPerson的类中查找到实现,此时会进入消息转发流程,返回的是libobjc.A.dylib _objc_msgForward
  • imp1是获取WJPerson的元类的sayGoodbye方法实现,sayGoodbye为是WJPerson的元类的实例方法,且已经进行了实现,所以可以获取到,imp4为+[WJPerson sayGoodbye]
    所以我们可以得出结论imp1imp4都不为空,所以推导出打印结果为 1 1 1 1
    看下实际打印结果
iMP_classToMetaclass - 1-1-1-1

你可能感兴趣的:(iOS中isa面试相关知识解析)