isa和superclass

/ 0评 / 0

isa指针

instanceisa 指向 class

classisa 指向 meta-class

meta-classisa 指向基类的 meta-class

superclass指针

classsuperclass 指向父类的 class,没有父类(NSObject就没父类)则为 nil

meta-classsuperclass 指向父类的 meta-class。基类的 meta-classsuperclass 指向基类的 class

isa和superclass指向图示

对象方法调用

当调用 对象方法 时,通过 instanceisa 指针找到 class,最后找到 对象方法 的实现进行调用。

如果在 class 中未找到 对象方法 的实现,则去 classsuperclass 中查找,以此类推。

// 伪代码
objc_msgSend(实例对象, @selector(对象方法));

类方法调用

当调用 类方法 时,通过 classisa 指针找到 meta-class,最后找到 类方法 的实现进行调用。

如果在 meta-class 中未找到 类方法 的实现,则去 meta-classsuperclass 中查找,以此类推。

// 伪代码
objc_msgSend(类对象, @selector(类方法));

类方法调用特殊情况

基类中不存在类方法,但存在同名对象方法:

@interface JFPerson : NSObject
@end

@implementation JFPerson
@end

@interface NSObject (Test)
+ (void)test;
@end

@implementation NSObject (Test)
- (void)test
{
    NSLog(@"-[NSObject test]: %p", self);
}
@end

调用类方法,没有报错:

NSLog(@"[JFPerson class]: %p", [JFPerson class]);
NSLog(@"[NSObject class]: %p", [NSObject class]);

[JFPerson test];
[NSObject test];

最终输出:

[JFPerson class]: 0x100008110
[NSObject class]: 0x20c402330
-[NSObject test]: 0x100008110
-[NSObject test]: 0x20c402330

这是因为在基类的 meta-class 中没有查找到对应的 类方法,就会通过基类的 meta-classsuperclass 指针,去基类的 class 中查找同名 对象方法

验证isa指向

NSObject *p1 = [[NSObject alloc] init];
Class pClass = [NSObject class];

通过 lldb 指令打印 isa 指向的地址和类对象地址值:

(lldb) p/x p1->isa
(Class) $0 = 0x010000020c402331 NSObject
(lldb) p/x pClass
(Class) $1 = 0x000000020c402330 NSObject

可以发现 isa 指向的并不直接是 class

这是因为在 32 位操作系统时期,实例对象的 isa 指针才是直接指向类对象的。

但是从 64 位开始,isa 需要进行一次位运算,才能计算出真实地址。

计算地址值

objc4isa.h 源码中可以拿到 ISA_MASK 的宏定义:

#if __arm64__
    #if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
        #define ISA_MASK 0x007ffffffffffff8ULL
    #else
        #define ISA_MASK 0x0000000ffffffff8ULL
    #endif
#elif __x86_64__
    #define ISA_MASK 0x00007ffffffffff8ULL
#endif

最终通过位运算,结论正确:

0x010000020c402331 & ISA_MASK == 0x000000020c402330;

classmeta-class 中的 isa 也是相同的计算方式。

了解类对象的结构

objc4objc-runtime-new.h 源码中:

typedef struct objc_class *Class;

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY; // isa指针
};

struct objc_class : objc_object {
    Class superclass;
    cache_t cache;            // 方法缓存
    class_data_bits_t bits;    // 用于获取具体的类信息

    // 存储的数据
    class_rw_t *data() const {
        return bits.data();
    }
};

struct class_rw_t {
    // 多个函数操作class_rw_ext_t结构体
};

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods; // 方法列表
    property_array_t properties; // 属性列表
    protocol_array_t protocols; // 协议列表
    char *demangledName;
    uint32_t version;
};

isKindOfClass和isMemberOfClass

BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];

NSLog(@"%d %d %d %d", res1, res2, res3, res4); // 1 0 0 0

objc4NSObject.mm 源码中可以获取到下面代码:

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

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

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

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

可以看出,如果是类方法,则是用自身对象的 isa 和传入对象比较,也就是对应的元类对象类对象进行比较,结果显而易见。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注