美文网首页
isa相关的经典面试题分析

isa相关的经典面试题分析

作者: 深圳_你要的昵称 | 来源:发表于2020-09-16 19:41 被阅读0次

前言

书接上回类的结构分析,我们得知isa指针的走位:对象 --> 类 --> 元类 --> 根元类-->根元类 指向自身。下面看看关于isa指针相关2道经典面试题

准备

先定义一个示例类LGPerson:

@interface LGPerson : NSObject

- (void)sayHello;
+ (void)sayByebye;

@end

@implementation LGPerson

- (void)sayHello {
    NSLog(@"LGPerson: say hello!");
}

+ (void)sayByebye {
    NSLog(@"LGPerson: say bye!");
}

@end

面试题一:isKindOfClass & isMemberOfClass

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

坑点分析:

  • isKindOfClassisMemberOfClass的区别
  • 请注意isKindOfClassisMemberOfClass类方法还是实例方法

1.底层分析isKindOfClass

先看实例方法:

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

很简单,当前实例对象的类[self class]与传递的入参类cls是否相等,是则返回YES,否则继续向上找父类 【for循环tcls->superclass】,直到找到为止,否则返回NO
for循环的方向是类-->父类-->(n 步)-->根类NSObject-->nil(此时for循环终止)
再看类方法:

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

与实例方法不同,类方法是当前类对象的self->ISA(),即元类,与传递的入参类cls是否相等,是则返回YES,否则继续向上找父类 【元类的父类】,继续【根元类】,继续【根元类的父类NSObject】,继续【NSObject的父类】是nil,循环终止,返回NO。
for循环的方向是元类-->元类的父类-->(n 步)-->根元类-->根元类的父类NSObject-->nil(此时for循环终止)

2.底层分析 isMemberOfClass

同样看源码:

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

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
  • 实例方法:判断当前的类[self class]是否和入参类cls相等。
  • 类方法是:判断当前的元类self->ISA()是否和入参类cls相等。

3.解答

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]];
  1. re1 和 re2的isKindOfClass类方法,根据for循环方向

元类-->元类的父类-->(n 步)-->根元类-->根元类的父类NSObject-->nil(此时for循环终止)

  • [NSObject class]的元类是根元类,根元类的父类就是[NSObject class],二者相等,结果为YES。
  • [LGPerson class]的元类[LGPerson meta class],二者不相等,继续循环,[LGPerson meta class] ->superclass = [LGPerson root meta class],也不相等,继续循环,[LGPerson root meta class]->superclass = NSObject根类,不相等,继续循环,NSObject根类->superclass = nil,循环终止,返回NO。
  1. re3 和 re4是实例方法,根据for循环方向

父类-->(n 步)-->根类NSObject-->nil(此时for循环终止)

  • [NSObject alloc]的类是[NSObject class],与入参[NSObject class]相同,返回YES。
  • [LGPerson alloc]的类是[LGPerson class],与入参[LGPerson class]相同,返回YES。

再看isMemberOfClass

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]];
  1. re5 和 re6的isMemberOfClass类方法,根据

元类self->ISA()是否和入参类cls相等

  • [NSObject class]的元类是`根元类,不同,返回NO。
  • [LGPerson class]的元类是[LGPerson meta class],不相同,返回NO。
  1. re7 和 re8的isMemberOfClass实例方法,根据

类[self class]是否和入参类cls相等

  • [NSObject alloc]的类是[NSObject class],与入参[NSObject class]相等,YES。
  • [LGPerson alloc]的类是[LGPerson class], 与入参[LGPerson class]相等,YES。

面试题二:实例方法与类方法的归属

根据isa指针的走位,我们知道,实例方法存储在类中,而类方法存储在元类中

1.class_getInstanceMethod

是获取类的实例方法

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(sayByebye));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayByebye));
    
    NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

1.LGPerson类中有实例方法- (void)sayHello;,所以method1有值,method2是元类中判断,所以无值
2.LGPerson类中的类方法+ (void)sayByebye;是在[LGPerson meta class]中,所以method3无值,method4有值

2.class_getClassMethod

是获取类的类方法

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(sayByebye));
    Method method4 = class_getClassMethod(metaClass, @selector(sayByebye));
    
    NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
  • - (void)sayHello;是实例方法,所以method1 和 method2都不成立,无值。
  • + (void)sayByebye;是类方法,看看method3,method4的值如何?
    运行看看
> - 0x0 -- 0x0 -- 0x100003148 -- 0x100003148

method3 method4都有值,why?查看源码:

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

可见,获取 类方法 就是获取 元类中的 实例方法,所以method3有值。
再看看cls->getMeta()

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

可见,在获取元类的时候,如果入参是元类,那么直接返回;否则才会继续this->ISA()查找元类。所以,method4表达式中,传入的已经是 Person 的元类, 那么会直接返回,而不是去找 Person元类的元类 。而 Person 元类中就是存在 + (void)sayByebye这样一个类方法的,所以 method4 是有值的。

延伸:imp

请思考,以下方法打印什么?

void lgIMP_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(sayByebye));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayByebye));

    NSLog(@"%s-%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}

下篇文章揭晓,哈哈!

相关文章

网友评论

      本文标题:isa相关的经典面试题分析

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