OC底层原理09 - 经典面试题分析

2020-09-30  本文已影响0人  卡布奇诺_95d2

面试题1

分析以下代码分别输出什么?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        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);
    }
    return 0;
}

isKindOfClass

类方法isKindOfClass

该方法主要是用来判断当前类的元类条件类是否相等。

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

实例方法isKindOfClass

该方法主要是用来判断当前对象所属的类条件类是否相等。

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

示例1

[(id)[NSObject class] isKindOfClass:[NSObject class]]

  1. 通过[NSObject class]分析得知,调用的是类方法isKindOfClass
  2. 第一次判断
    clsNSObject类
    tclsNSObject元类
    cls 不等于 tcls
    修改tclsNSObject元类的父类,即:NSObject类
  3. 第二次判断
    clsNSObject类
    tclsNSObject类
    cls 等于 tcls

结果:返回YES

示例2

[(id)[LGPerson class] isKindOfClass:[LGPerson class]]

  1. 通过[LGPerson class]分析得知,调用的是类方法isKindOfClass
  2. 第一次判断
    clsLGPerson类
    tclsLGPerson元类
    cls 不等于 tcls
    修改tclsLGPerson元类的父类,即根元类NSObject
  3. 第二次判断
    clsLGPerson类
    tcls根元类
    cls 不等于 tcls
    修改tclsNSObject元类的父类,即:NSObject类
  4. 第三次判断
    clsLGPerson类
    tclsNSObject类
    cls 不等于 tcls
    修改tclsNSObject类的父类,即:nil
  5. 由于tclsnil,循环结束。

结果:返回NO

示例3

[(id)[NSObject alloc] isKindOfClass:[NSObject class]]

  1. 通过[NSObject alloc]分析得知,调用的是对象方法isKindOfClass
  2. 第一次判断
    clsNSObject类
    tclsNSObject类
    cls 等于 tcls

结果:返回YES

示例4

[(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]

  1. 通过[LGPerson alloc]分析得知,调用的是对象方法isKindOfClass
  2. 第一次判断
    clsLGPerson类
    tclsLGPerson类
    cls 等于 tcls

结果:返回YES

isMemberOfClass

类方法isMemberOfClass

该方法主要用来判断当前类的元类条件类是否相等。

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

实例方法isMemberOfClass

该方法主要用来判断当前对象所属的类条件类是否相等。

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

示例5

[(id)[NSObject class] isMemberOfClass:[NSObject class]]

  1. 通过[NSObject class]分析得知,调用的是类方法isMemberOfClass
  2. 当前元类NSObject元类条件类NSObject

结果:不相等,返回NO

示例6

[(id)[LGPerson class] isMemberOfClass:[LGPerson class]]

  1. 通过[LGPerson class]分析得知,调用的是类方法isMemberOfClass
  2. 当前元类LGPerson类的元类条件类LGPerson类

结果:不相等,返回NO

示例7

[(id)[NSObject alloc] isMemberOfClass:[NSObject class]]

  1. 通过[NSObject alloc]分析得知,调用的是实例方法isMemberOfClass
  2. 当前对象类NSObject类条件类NSObject类

结果:相等,返回YES

示例8

[(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]

  1. 通过[LGPerson alloc]分析得知,调用的是实例方法isMemberOfClass
  2. 当前对象类LGPerson类条件类LGPerson类

结果:相等,返回YES

面试题1输出结果:

2020-09-30 14:59:08.857321+0800 KCObjc[72747:9401735]  
 re1 :1
 re2 :0
 re3 :1
 re4 :1
2020-09-30 14:59:12.976812+0800 KCObjc[72747:9401735]  
 re5 :0
 re6 :0
 re7 :1
 re8 :1
Program ended with exit code: 0

总结

  1. 类方法 isKindOfClass 和 isMemberOfClass
    相同:比较 元类 与条件类。
    不同:isKindOfClass 在第一次比较不成功之后,会继续查找元类的父类,直至找到nil,而 isMemberOfClass 在比较不成功之后直接返回false。
  2. 实例方法 isKindOfClass 和 isMemberOfClass
    相同:比较 类与 条件类
    不同:isKindOfClass 在第一次比较不成功之后,会继续查找类的父类,直至找到nil,而 isMemberOfClass 在比较不成功之后直接返回false。

面试题2

创建一个 Person 类,继承自 NSObject,分别添加一个实例方法和一个类方法

@interface LCPerson : NSObject

- (void)sayHello;
+ (void)say666;

@end

@implementation LCPerson

- (void)sayHello{
    NSLog(@"sayHello");
}

+ (void)say666{
    NSLog(@"say666");
}

@end

分析以下代码分别输出什么?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        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));
        
        NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
    }
    return 0;
}

class_getInstanceMethod

该方法主要是用于获取实例方法如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL。针对该方法,苹果有如下说明

image.png

根据之前的分析,
实例方法是存储在对象所属的类中
类方法是存储元类中

分析

面试题2输出结果:

2020-09-30 15:01:41.051611+0800 KCObjc[72806:9404232] 
 method1 :0x100008100
 method2 :0x0
 method3 :0x0
 method4 :0x100008098
Program ended with exit code: 0

面试题3

此题是对面试题2的延伸,因此准备条件与面试题2一样。
分析以下代码分别输出什么?

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        Class pClass     = object_getClass(person);
        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));
        
        NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
    }
    return 0;
}

class_getClassMethod

该方法主要是用于获取类方法,一个指向方法数据结构的指针,它指向类指定的类方法的实现如果指定的类或它的父类不包含具有指定的类方法,则为NULL。针对该方法,苹果有如下说明

image.png

分析

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

由源码知道,获取类方法其实就是获取元类的实例方法

面试题3输出结果:

2020-09-30 15:12:42.559984+0800 KCObjc[72925:9410227] 
 method1 :0x0
 method2 :0x0
 method3 :0x100008098
 method4 :0x100008098
Program ended with exit code: 0

总结

上一篇下一篇

猜你喜欢

热点阅读