Objective-C底层面试题总结

2020-12-24  本文已影响0人  丸疯

方法的归属问题探索

  1. 定一个Person类,定义一个实例方法,一个类方法,并完成实现
@interface Person : NSObject
- (void)sayHello;
+ (void)sayHappy;

@end

@implementation Person

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

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

@end
  1. 获取类的方法并打印出来
void objc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        NSLog(@"Method, name: %@", key);
    }
    free(methods);
}
  1. 获取实例方法并打印其地址
void instanceMethod_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));
    
    NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
  1. 获取类方法并打印其地址
void classMethod_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));
    // 元类 为什么有 sayHappy 类方法 0 1
    //
    Method method4 = class_getClassMethod(metaClass, @selector(esd));
    
    NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
  1. 获取方法实现
void IMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}
  1. 方法调用
        Person *person = [Person alloc];
        Class pClass     = object_getClass(person);
        objc_copyMethodList(pClass);

        instanceMethod_classToMetaclass(pClass);
        classMethod_classToMetaclass(pClass);
        IMP_classToMetaclass(pClass);
  1. 执行结果
Method, name: sayHello
instanceMethod_classToMetaclass - 0x1000081b0-0x0-0x0-0x100008148
classMethod_classToMetaclass-0x0-0x0-0x100008148-0x0
IMP_classToMetaclass-0x100003d10-0x7fff67dc7bc0-0x7fff67dc7bc0-0x100003d40

接下来我们逐个函数进行分析,然后论证结果

objc_copyMethodList

从这个方法里面我们知道,Person中只存储了实例方法

class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)

从文档中我们可以看出,当查找实例方法的时候,会沿着当前类的继承链找下去。


class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)

从文档上我们可以看出,也是通过父类是否有class method来判断

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

看了源码,其实是去获取元类的实例化方法。如果当前类是元类就直接使用当前类,来完成class_getInstanceMethod,这里不继续通过→ISA()来继续往下摸,是因为会产生无限循环。我们知道class_getInstanceMethod会根据继承链一直去寻找实例方法。


class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

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

    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    // Translate forwarding function to C-callable external version
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

通过源码,首先去查找方法实现,如果没有找到,则进行消息转发。


iskindOfClass & isMemberOfClass

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL re3 = [(id)[Person class] isKindOfClass:[Person class]];
    BOOL re4 = [(id)[Person class] isMemberOfClass:[Person class]];
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

输出如下

 re1 :1
 re2 :0
 re3 :0
 re4 :0
    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
    BOOL re7 = [(id)[Person alloc] isKindOfClass:[Person class]];
    BOOL re8 = [(id)[Person alloc] isMemberOfClass:[Person class]];

输出:

 re5 :1
 re6 :1
 re7 :1
 re8 :1

为什么会是这个结果,我们来分析一下isKindOfClassisMemberOfClass的源码

  1. isKindOfClass类方法源码
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

通过源码我们可以看到,当isKindOfClass作为类方法的时候,是先通过ISA获取当前类的元类,然后递归当前的元类继承链和当前类进行对比。


  1. isKindOfClass实例方法源码
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
// class的实例方法
- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

通过源码,当isKindOfClass作为实例方法的时候,先获取当前实例对象的isa当前实例的类,与传入类进行对比,然后再把传入类与当前实例对象的类继承链来进行对比


!!!注意这里有坑点
看上去分析的很对,实际上是这样的么?我们进行断点调试的时候发现isKindOfClass不走我们上面分析的两个方法,而是统一走objc_opt_isKindOfClass

isKindOfClass的调用.png
😢接着我们来分析objc_opt_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);
}

看到源码,是不是和上面分析的是基本一致的,首先通过需要判断的对象或者类的isa来获取当对象的类或者类的元类,然后通过他们的继承链来与需要对比的类进行对比
Tips: 类通过isa可以获取元类,对象通过isa获取实例当前对象的类

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

通过源码,拿元类和传入类来进行对比


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

// class的实例方法
- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

objc_object::getIsa() 
{
    if (fastpath(!isTaggedPointer())) return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}

分析源码,[self class]其实就是通过isa去获取实例化当前对象的类,然后与传入类来进行对比。

上一篇 下一篇

猜你喜欢

热点阅读