iOS底层探索--面试题分析(isa走向图)

386 阅读4分钟

小谷底层博客合集

  • 今天通过isa走向图解决2到经典的面试题(这两道面试题可以说明你是否研究过底层,是否了解isa走位,而且还TM有坑!)

  • 我们先把超级经典的isa走向图啪出来:

1. 面试题1

要是这么长的面试题估计大家会犯慌!慢慢分析就好!反正没有时间规定!

还有:不知道如何看源码环境请看另一篇博客:objc4-787.1编译调试

  • 上代码:
@interface XGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end

@implementation XGPerson
- (void)sayHello{
    NSLog(@"XGPerson say : Hello!!!");
}
+ (void)sayHappy{
    NSLog(@"XGPerson say : Happy!!!");
}
@end

void xgClassMethod_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));
    // 
    NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

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

问:0代表无结果,1代表有结果,输出的是什么?

  • 解释:
  • 我们开始分析习题!
  • 1、 主要方法是:xgClassMethod_classToMetaclass
  • 2、 最终结果就是查看 method 是否存在
      1. 我们开始探究xgClassMethod_classToMetaclass
      1. 首先前两行代码就是获取元类metaClass
      1. 我们可以先看下class_getClassMethod的源码:
    Method class_getClassMethod(Class cls, SEL sel)
    

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

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

} //这个本质就是查看他的元类是否有实例方法!

* 4. 然后我们可以观察下`cls`如何`getMeta()`
```Objc
Class getMeta() {
     if (isMetaClass()) return (Class)this;
     else return this->ISA();
 }
    1. 我们所需要的信息都已经准备好了。那么我们可以找答案了!!
    1. 答案解析!

  1. method1是查看 pClass 是否有 sayHello,sayHello是实例方法,存在类中!然后通过源码知道:class_getInstanceMethod(cls->getMeta(), sel);他去他的元类中找 sayHello所以找不到!所以是0

  1. method2是查看metaClass是否有sayHello,sayHello是实例方法,去元类的元类中(getMeta,是元类的话返回自己了)找 sayHello所以找不到!所以是0

  1. method3中查看:首先sayHappy是类方法!method3的意思就是在pClass的元类中是否有实例方法sayHappy,所以是1

  1. method4就有点意思了!,sayHappy是类方法!,这个要在元类的元类里面找!,就要看isa的走向图了!,而且getMeta的实现:如果是元类就返回自己!总体来说的意思是:就是从元类中找实例方法sayHappy,所以答案是1
    1. 查看输出:
    1. 答案:xgClassMethod_classToMetaclass-0-0-1-1

类中存储实例方法,元类中存储类方法(以实例方法的形式)

2. 面试题2

  • 出题上代码:(问:输出的是啥)
		BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; 
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
        BOOL re3 = [(id)[XGPerson class] isKindOfClass:[XGPerson class]]; 
        BOOL re4 = [(id)[XGPerson class] isMemberOfClass:[XGPerson 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)[XGPerson alloc] isKindOfClass:[XGPerson class]]; 
        BOOL re8 = [(id)[XGPerson alloc] isMemberOfClass:[XGPerson class]];
        NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
  • 来来来,老规矩,咱们吧需要的源码全都粘出来!

// isKindOfClass 在编译是llvm处理过:objc_opt_isKindOfClass

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}


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

//实例方法
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • 好了!又来分析了!看我答案之前大家最好想一波!

    1. 根据源码调试:类方法和实例方法isKindOfClass都会进入:objc_opt_isKindOfClass.

    • 1.1. 当是类方法的时候,class1 isKindOfClass: class2objclass1objisa指向的类的父类,相等于 class2, 返回YES。

    • 1.2. 是实例方法时:object1 isKindOfClass class: objobject1,根据isa流程图可知:obj的isa指向创建他的类,所以意思是:object1是否是class本类子类创建的对象.

所以类方法可以得知re1re3的值:

re1:NSObject--(isa)-->NSObject(元类)——(继承)——>NSObject

NSObject == NSObject! 所以是1

re3:XGPerson--(isa)-->XGPerson(元类)--(继承)-->NSObject

XGPerson == NSObject! 所以是0。

所以实例方法可以得知re5re7

re5: [NSObject alloc]NSObject创建的对象

re7: [XGPerson alloc]XGPerson创建的对象

所以都是1

扩展:BOOL re7 = [(id)[XGPerson alloc] isKindOfClass:[NSObject class]];这样的话,re7也为1


    1. 分析类方法:A isMemberOfClass B:就是Aisa是否与B相同

** 类和元类的名称可能叫法相同,但是不是一个东东啊!! **

所以可以得知re2re4的值:

re2:NSObject--(isa)-->NSObject(元类)

re4:XGPerson--(isa)-->XGPerson(元类)

所以都是0


    1. 实例方法 object2 isMemberOfClass class2: [object2 class]是否与class2相同

所以可以得知re6re8

re6:[NSObject alloc].class == NSObject

re8:[XGPerson alloc].class == XGPerson

所以都是1

兄弟们!!这两道面试题解释的有点长!!,希望对大家有帮助~~,我也是这么一步步解释才明白的!