OC底层-面试题小试牛刀

957 阅读4分钟

关于isa的走位和的继承关系

isKindOfClass & isMemberOfClass

   // 面试题 1
        BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
        BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
        
        BOOL re3 = [(id)[MuPerson class] isKindOfClass:[MuPerson class]];
        BOOL re4 = [(id)[MuPerson class] isMemberOfClass:[MuPerson class]];
        
        BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
        BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
        
        BOOL re7 = [(id)[MuPerson alloc] isKindOfClass:[MuPerson class]];
        BOOL re8 = [(id)[MuPerson alloc] isMemberOfClass:[MuPerson class]];
        

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);
}
  • isKindOfClass底层源码实现objc_opt_isKindOfClass,这里有个细节llvm在编译阶段做了处理,使得isKindOfClassobjc_opt_isKindOfClass。方法内部实现:拿当前的传入对象的isa,呢么分为2种情况:

    • 1 、传入的obj是类,类的isa就是类的元类,如果存在,就和当前的类的对比。如果不相等,就拿类的元类的父类(根元类),甚至于类的元类的父类的父类(NSObject),直到niltip:这种情况的对比链就是 元类的继承链。
    • 2、传入的obj是类的对象,类对象的isa就是类本身,如果存在,就和当前的类对比。如果不相等,就拿类的父类,甚至于类的父类的父类,直到NSObjectniltip:这种情况的对比链就是 的继承链。所以是一定成立的。

isMemberOfClass

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

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  • isMemberOfClass类方法实现:拿当前的类的元类,和当前的类对比。tip:一定不成立,类的元类和当前的类肯定不相等。
  • isMemberOfClass实例化方法实现:拿当前的对象的类,如果存在,就和当前的类对比。tip:一定成立,当前的对象的类肯定和当前的类是相等的。

isKindOfClass && isMemberOfClass

  • 从源码的实现上,我们可以看到,isMemberOfClass其实只判断了isKindOfClass判断链的第一步,就返回了。

面试题1分析

  • 除了1、3需要我们分析,其他的都是干扰项。5和7一定成立,2和4一定不成立,6和8都是一定成立的。
  • 分析1,otherClass = NSObject, tcls = {NSObject->ISA() 也就是 根元类 ->NSObject -> nil},NSObject 先和 根元类 对比,再和 NSObject 对比,最终返回YES
  • 分析3,otherClass = MuPerson, tcls = {MuPerson->ISA() 也就是 元类 -> 根元类 -> NSObject -> nil},MuPerson 依次和 元类、根元类、NSObject、nil对比,最终返回NO

方法的追踪

 // 面试题 2
 @interface MuPerson : NSObject
 
-  (void)funcHello;
+ (void)funcWorld;

@end

-  (void)funcHello{
    NSLog(@"hello");
}

+ (void)funcWorld{
    NSLog(@"world");
}


        MuPerson *person = [MuPerson alloc];
        Class pClass     = object_getClass(person);
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getInstanceMethod(pClass, @selector(funcHello));
        Method method2 = class_getInstanceMethod(metaClass, @selector(funcWorld));
        
        Method method3 = class_getInstanceMethod(pClass, @selector(funcHello));
        Method method4 = class_getInstanceMethod(metaClass, @selector(funcWorld));
        
        Method method5 = class_getClassMethod(pClass, @selector(funcHello));
        Method method6 = class_getClassMethod(metaClass, @selector(funcHello));

        Method method7 = class_getClassMethod(pClass, @selector(funcWorld));
        Method method8 = class_getClassMethod(metaClass, @selector(funcWorld));
    
        IMP imp1 = class_getMethodImplementation(pClass, @selector(funcHello));
        IMP imp2 = class_getMethodImplementation(metaClass, @selector(funcHello));
        
        IMP imp3 = class_getMethodImplementation(pClass, @selector(funcWorld));
        IMP imp4 = class_getMethodImplementation(metaClass, @selector(funcWorld));

class_getInstanceMethod

  • 从官方文档上可以看到,class_getInstanceMethod是为了查找当前乃至这个父类们有没有这个实例化方法

class_getClassMethod

  • 从官方文档上可以看到,class_getClassMethod是为了查找当前乃至这个父类们有没有这个类方法

这里可能大家会觉得和class_getInstanceMethod一样,其实不然,看下源码实现:

//获取类方法
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

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

⬇️
//获取元类
 // NOT identical to this->ISA when this is a metaclass 
 判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}
  • 可以得出class_getClassMethod的实现是获取类方法,本质为是获取元类实例方法,最终还是会走到class_getInstanceMethod,但是在这里有一个细节:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this当前的元类,其目的是为了防止在查找到根元类的时候,根元类的isa还是指向了自身的无限递归查找

class_getMethodImplementation

  • 从官方文档上可以看到,class_getMethodImplementation是为了查找当前方法IMP指针。如果在当前类找到了就返回IMP指针,没有找到就返回消息转发指针

面试题2解析

  • class_getInstanceMethod的1、2、3、4
    • 1、funcHello实例化方法,MuPersonbits存的有
    • 2、funcHello实例化方法,MuPerson的元类的bits中没有
    • 3、funcWorld类方法,MuPersonbits中没有
    • 4、funcWorld类方法,MuPerson的元类的bits中有
  • class_getClassMethod的5、6、7、8
    • 1、pClassMuPerson不是元类,向下查找MuPerson的元类, funcHello实例化方法,MuPerson的元类的bits中没有
    • 2、 metaClass为MuPerson的元类,则不向下查找。funcHello实例化方法,MuPerson的元类的bits中没有
    • 3、pClassMuPerson不是元类,向下查找MuPerson的元类, funcWorld类方法,MuPerson的元类的bits中有
    • 4、 metaClass为MuPerson的元类,则不向下查找。funcWorld类方法,MuPerson的元类的bits中有
  • class_getMethodImplementation的1、2、3、4
    • 1、funcHello实例化方法,MuPersonbits存的有,则返回IMP
    • 2、funcHello实例化方法,MuPerson的元类的bits中没有,则返回消息转发指针
    • 3、funcWorld类方法,MuPersonbits中没有,则返回消息转发指针
    • 4、funcWorld类方法,MuPerson的元类的bits中有,则返回IMP