runtime面试题分析

前言

此篇博客需要用到 isasuperclass 的指向流程分析,如果你对此还不是很清晰,建议你先通过这篇博客 看透 isa 了解一下 ;如果你已有所掌握,在这里,我们先做一个简短的回顾。

runtime面试题分析_第1张图片
isa与superclass流程图

isa 的指向:对象的 isa 指向 ; 类的 isa 指向 元类;元类的 isa 指向 根元类;根元类的 isa 指向 自己

类的superclass 的指向:类的 superclass 指向 父类, 父类的 superclass 指向 根类 ,根类的superclass 指向 nil

元类的superclass 的指向:元类的 superclass 指向 父类的元类,父元类的 superclass 指向 根类的元类 根元类的 superclass 指向 根类 根类的 superclass 指向 nil

示例分析

  1. 下面的代码输出什么内容?
  // Person 继承 NSObject
  @implementation Person : NSObject

  BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       
  BOOL re2 = [(id)[Person class]   isKindOfClass:[Person class]]; 
  BOOL re3 = [(id)[NSObject new]   isKindOfClass:[NSObject class]]; 
  BOOL re4 = [(id)[Person new]     isKindOfClass:[Person 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)[Person class]   isMemberOfClass:[Person class]];     
  BOOL re7 = [(id)[NSObject new]   isMemberOfClass:[NSObject class]];     
  BOOL re8 = [(id)[Person new]     isMemberOfClass:[Person class]];     

       NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);

你的答案是什么呢?不妨先停一停,多花些时间思考一下你的答案,然后再继续往下看。


in thinking .......

then

go on


此题主要考察两个方面的内容,① isasuperclass 的指向流程;② (+ / -) isKindOfClass(+ / - ) isMemberOfClass 的实现细节。

re1re2 的第一个参数都是 ,所以是对 + (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
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];

传入的cls 为 NSobject, self 指向 NSobject,进入循环

①第一次循环: tcls 为 NSobject meta ,cls 为 NSobject ;执行判断条件if (tcls == cls) ,不相等;执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject meta 的父类 ,即 NSObject。进入第二次循环。

②第二次循环:此时 tcls 为 NSobject,cls 依然是 NSobject,执行判断条件 if (tcls == cls) 相等,return YES。

所以 re1 的结果为 1

  • re2
BOOL re2 = [(id)[Person class]   isKindOfClass:[Person class]]; 

传入的cls为 NSobject,self指向 Person,进入循环

①第一次循环:tcls 为 Person meta,cls 为 Person类; 执行判断条件 if (tcls == cls) ,不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject metal。进入第二次循环。

②第二次循环: tcls 为 NSobject meta ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSObject。进入第三次循环。

③第三次循环: tcls 为 NSobject ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 nil。不满足for循环执行条件 tcls。结束循环。

所以 re2 的结果为 0

+ (BOOL)isKindOfClass:(Class)cls 考察的是:判断类或类的父类的类型是否是条件类

re3re4 的第一个参数都是 对象,所以是对 -(BOOL)isKindOfClass:(Class)cls 方法的考察。

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

传入的cls 为 NSObject 类,self 指向 NSObject 的 实例对象

①第一次循环:tcls 指向 NSObject 类,cls 为 NSObject 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。

所以 re3 返回1。

  • re4
BOOL re4 = [(id)[Person new]     isKindOfClass:[Person class]];

传入的cls 为 Person 类,self 指向 Person 的 实例对象

①第一次循环:tcls 指向 Person 类,cls 为 Person 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。

所以 re4 返回1。

-(BOOL)isKindOfClass:(Class)cls 考察的是 判断对象的类 是否是条件类的子类。

re5re6 的第一个参数都是 类,所以是对 + (BOOL)isMemberOfClass:(Class)cls 方法的考察。

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}
  • 先看 re5
BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  

传入的cls 为 NSObject, self 指向 NSObject

self->ISA( ) ,self的 isa 指向 NSObject metaNSObject metaNSObject 不相等。

所以 re5 的结果为 0

  • re6
 BOOL re6 = [(id)[Person class]   isMemberOfClass:[Person class]];  

传入的cls 为 Person, self 指向 Person

self->ISA( ) ,self的 isa 指向 Person metaPerson metaPerson 不相等。

所以 re6 的结果为 0

+ (BOOL)isMemberOfClass:(Class)cls 考察的是类的元类 是否是条件类。

re7re8 的第一个参数都是 对象 所以是对 -(BOOL)isMemberOfClass:(Class)cls 方法的考察。

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • re7
BOOL re7 = [(id)[NSObject new]   isMemberOfClass:[NSObject class]]; 

传入的cls 为 NSObject, self 指向 NSObject 对象

[self class] 为 NSObject 类 ;与 cls 相等。

所以 re7 的结果为 1

  • re8
BOOL re8 = [(id)[Person new]     isMemberOfClass:[Person class]]; 

传入的cls 为 Person, self 指向 Person 对象

[self class] 为 Person 类 ;与 cls 相等。

所以 re8 的结果为 1

-(BOOL)isMemberOfClass:(Class)cls 考察的是对象 是否是条件类的实例。

  1. 方法的存储示例

创建 Person类 继承自 NSObject,实现方法如下:

@implementation Person

- (void)sayHello{
    NSLog(@"Person say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"Person say : Happy!!!");
}

@end

分别用class_getInstanceMethodclass_getClassMethod 获取 objc_method

下面输出的结果会是什么?

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

在类的结构分析中,我们知道,对象的 实例方法 存储在 中,类方法 存储在 元类 之中。

对于method1 :在 Person类 中查找- (void)sayHello 这样一个实例方法 ,显然是存在的。

对于method2 :在 Person元类 中查找- (void)sayHello 这样一个实例方法 ,是不存在的。

对于method3 :在 Person类 中查找+ (void)sayHappy 这样一个实例方法 ,是不存在的。

对于method4 :在 Person元类 中查找+ (void)sayHappy 这样一个实例方法 ,是存在的。

所以上述结果为:> - 0x1000031b0 -- 0x0 -- 0x0 -- 0x100003148

继续看下面的输出结果:

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

我们直接输出结果: > - 0x0 -- 0x0 -- 0x100003148 -- 0x100003148

对于method1 :在 Person类 中查找- (void)sayHello 这样一个类方法 ,不存在。

对于method2 :在 Person元类 中查找- (void)sayHello 这样一个类方法 ,不存在。

对于method3 :在 Person类 中查找+ (void)sayHappy 这样一个类方法 ,存在。

对于method4 :在 Person元类 中查找+ (void)sayHappy 这样一个类方法 ,存在。

对于method4,为什么 Person元类 中会 + (void)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();
    }

可见,在获取元类的时候,如果传入的就是元类,那么会返回本身;否则才会继续查找元类,而对于这个例子 Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));

传入的已经是 Person 的元类, 那么会直接返回 Person 元类,而不是去找 Person元类的元类 。而 Person 元类中就是存在 + (void)sayHappy 这样一个类方法的,所以 method4 是有返回值的。

为什么这样设计呢?

因为在现实世界中,没人会向元类对象发送消息。

  1. 下面的代码输出什么内容?
@implementation Son : Father
- (id)init {
    self = [super init];
    if (self) {
        Class son = [self class];
        Class Father = [super class];
        NSLog(@"%@", NSStringFromClass(Son));
        NSLog(@"%@", NSStringFromClass(Father));
    }
    return self;
}
@end

分析流程:首先我们将 Son.m 编译成 c++ 文件

clang -rewrite-objc Son.m -o Son.cpp

在c++中,看一下是如何实现的。

// @implementation Son

static id _I_Son_init(Son * self, SEL _cmd) {
    self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"));
    if (self) {
        
        Class son = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
        
        Class Father = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"));
        
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_m__6whbhm2540q9gqrsnmnkmkg40000gn_T_Son_58884b_mi_0, NSStringFromClass(son));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_m__6whbhm2540q9gqrsnmnkmkg40000gn_T_Son_58884b_mi_1, NSStringFromClass(Father));
    }
    return self;
}
// @end

我们关注这两段代码:

Class son = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
        
Class Father = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"));

objc中 super 是编译器标示符,并不像 self 一样是一个对象,遇到向 super 发的方法时会转译成 objc_msgSendSuper(...),而参数中的对象还是 self,于是从父类开始沿继承链寻找 - class 这个方法,最后在NSObject中找到(若无override),此时,[self class][super class] 已经等价了。

所以 输出的结果 都是 "Son"。

你可能感兴趣的:(runtime面试题分析)