老青菜

iOS isa 底层分析

2019-12-17

我们先来看一个问题,下面的代码会输出什么?

@implementation Son : Father
- (id)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

答案:都输出 Son。

Son
Son

如果不了解isa的话,可能对这个结果会有点疑惑,+class-class方法里到底做了什么呢?

isa 指向

我们先来看下+class-class内部的实现:

//NSObject.mm   
@implementation NSObject
+ (Class)class {
    return self ;
}

- (Class)class {
    return object_getClass(self);
}

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

类的class方法返回了类对象自身,对象的class方法返回了isa指针。

而在对象和类的结构里,都有isa的身影。

// rumtime.h
typedef struct objc_class *Class;
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
    //.....
}

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
    //.....
};

那么isa到底指向什么?

class isa

我们先来看看classisa指向的是什么,我们从class的创建函数里找到一些关键信息:

//objc-runtime-new.mm
Class objc_allocateClassPair(Class superclass, const char *name, 
                             size_t extraBytes) {
    Class cls, meta;
    rwlock_writer_t lock(runtimeLock);
    // Fail if the class name is in use.
    // Fail if the superclass isn't kosher.
    if (getClass(name)  ||  !verifySuperclass(superclass, true/*rootOK*/)) {
        return nil;
    }
    // Allocate new classes.
    //创建 cls、meta 两个class
    cls  = alloc_class_for_subclass(superclass, extraBytes);
    meta = alloc_class_for_subclass(superclass, extraBytes);
    // fixme mangle the name if it looks swift-y?
    //初始化cls、meta,并设置关联。
    objc_initializeClassPair_internal(superclass, name, cls, meta);
    return cls;
}

objc_allocateClassPair提供了创建class的功能,方法内部生成了clsmeta两个class,并调用objc_initializeClassPair_internal进行初始化。我们继续看函数内部。

static const uint8_t UnsetLayout = 0;
static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta) {
    runtimeLock.assertWriting();
    class_ro_t *cls_ro_w, *meta_ro_w;
    cls->cache.initializeToEmpty();
    meta->cache.initializeToEmpty();
    //省略部分代码...
    // Set basic info
    cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
    meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
    cls->data()->version = 0;
    meta->data()->version = 7;
    cls_ro_w->flags = 0;
    meta_ro_w->flags = RO_META;
    //省略部分代码...
    cls_ro_w->name = strdup(name);
    meta_ro_w->name = strdup(name);
    cls_ro_w->ivarLayout = &UnsetLayout;
    cls_ro_w->weakIvarLayout = &UnsetLayout;
    // Connect to superclasses and metaclasses
    cls->initClassIsa(meta);
    if (superclass) {
        //有父类
        meta->initClassIsa(superclass->ISA()->ISA());
        cls->superclass = superclass;
        meta->superclass = superclass->ISA();
        addSubclass(superclass, cls);
        addSubclass(superclass->ISA(), meta);
    } else {
        //没有父类
        meta->initClassIsa(meta);
        cls->superclass = Nil;
        meta->superclass = cls;
        addSubclass(cls, meta);
    }
}

内容比较多,大致来看,objc_initializeClassPair_internal做了5个事情,

  1. 初始化clsmetanamedataversion等等属性。
  2. 连接了clsmetacls->isa指向meta
  3. 设置meta->isa

    • clssuperclassNSObject),那么meta->isa指向meta自身。
    • clssuperclass,那么meta->isa指向superclass->isa->isa,其实也就是NSObject meta
  4. 设置meta->superclass

    • clssuperclassNSObject),那么meta->superclass指向cls
    • clssuperclassNSObject),那么meta->superclass指向superclass->isa
  5. 设置cls->superclass

这样可能有点晕,我们来举个例子,假如我们现在有GrandsonSonFather3个Class,前者依次继承后者,我们来分析一下各自的isasuperclass是如何关联的。

  1. 初始化class,生成对应的clsmeta两个class,并初始化namedataversion等等属性。

  2. 调用 cls->initClassIsa(meta),关联cls、meta

    • Father class->isa指向Father meta
    • Son class->isa指向Son meta
    • Grandson class->isa指向Grandson meta
  3. 调用meta->initClassIsa(superclass->ISA()->ISA()) ,设置 meta isa

    • Father meta->isa指向NSObject class->isa->isa,也就是NSObject meta->isa,即NSObject meta自己(NSObject没有superclass)。
    • Son meta->isa指向Father class->isa->isa,也就是Father meta->isa,即NSObject meta
    • Grandson meta->isa指向Son class->isa->isa,也就是Son meta->isa,也就是步骤4,最后也是NSObject meta
  4. 调用meta->superclass = superclass->ISA(),设置meta superclass

    • NSObject meta->superclass指向NSObject
    • Father meta->superclass指向NSObject isa,即NSObject meta
    • Son meta->superclass指向Father isa,即Father meta
    • Grandson meta->superclass指向Son isa,即Son meta

object isa

弄清楚classmeta之间的关系后,我们再来看看object里的isa是如何设置的。
首先我们来看下alloc init的底层实现。

//NSObject.h
@interface NSObject <NSObject>
+ (id)alloc {
    return _objc_rootAlloc(self);
}
@end

//NSObject.mm
id _objc_rootAlloc(Class cls) {
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id 
callAlloc(Class cls, bool checkNil, bool allocWithZone=false) {
    if (checkNil && !cls) return nil;
#if __OBJC2__
    if (! cls->ISA()->hasCustomAWZ()) {
        if (cls->canAllocFast()) {
            // obj->isa = cls,即对象的isa指向class。
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (!obj) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

id class_createInstance(Class cls, size_t extraBytes) {
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

static __attribute__((always_inline))  id 
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil) {
    if (!cls) return nil;
    //省略部分代码...
    id obj;
    if (!UseGC  &&  !zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        // obj->isa = cls,即对象的isa指向class。
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        //省略部分代码...
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        // obj->isa = cls,即对象的isa指向class。
        obj->initIsa(cls);
    }
    //省略部分代码...
    return obj;
}

这里我省略了部分无关代码,方法调用层级还是有点多,但是不难发现,最后都是调用了initIsa或者initInstanceIsa,把obj->isa指向了cls,所以得到:

obj->isa = cls

class & meta

到这里,isa指向关系我们大概清楚了,但是classmeta有什么区别吗?

  1. class类对象,包含了isasuperclass、变量、协议、实例方法等等成员。
  2. meta元类对象,包含了isasuperclass、静态方法列表等成员。

通过代码来验证一下他们的结构:

//首页声明 Father class
@interface Father : Father
@property (nonatomic, strong) NSString *name;
@end
@implementation Father
static NSString *nickname = @"";
- (void)eta {}
+ (void)clothe {}
@end

///打印变量
- (void)printIvars:(Class )cls {
    unsigned int count = 0;
    Ivar *members = class_copyIvarList(cls, &count);
    for(int i = 0; i < count; i++) {
        Ivar ivar = members[i];
        const char *memberName = ivar_getName(ivar);
        NSLog(@"%@,%p,%@", cls, cls,  [NSString stringWithCString:memberName encoding:NSUTF8StringEncoding]);
    }
    free(members);
}
///打印方法
- (void)printMethods:(Class )cls {
    unsigned int methodCount;
    Method *methodList = class_copyMethodList(cls, &methodCount);
    unsigned int i = 0;
    for (; i < methodCount; i++) {
      NSLog(@"%@,%p,%@", cls, cls, [NSString stringWithCString:sel_getName(method_getName(methodList[i])) encoding:NSUTF8StringEncoding]);
    }
    free(methodList);
}
//打印class、meta的成员
- (void)test {
    Father *obj1 = [[Father alloc] init];
    NSLog(@"==");
    [self printIvars:obj1.class];
    [self printMethods:obj1.class];
    NSLog(@"==");
    [self printIvars:object_getClass(obj1.class)];
    [self printMethods:object_getClass(obj1.class)];
}

输出日志:

==
Father,0x10673c4d8,_name
Father,0x10673c4d8,name
Father,0x10673c4d8,.cxx_destruct
Father,0x10673c4d8,setName:
Father,0x10673c4d8,eta
==
Father,0x10673c4b0,clothe

很明显的看到,class类对象里包含了实例方法、变量等等成员,meta只包含了静态方法。

结论

现在我们对isa指针比较清楚了,这里再做一个总结:

  1. instance->isa = class
  2. class->isa = meta
  3. meta->isa = superclass->isa->isa = NSObject meta
    • NSObject meta->isa = NSObject meta
  4. meta->superclass = superclass->isa
    • NSObject meta->superclass = NSObject class
  5. class->superclass = superclass
    • NSObject class->superclass = nil

再看这张图片就很容易理解了。

验证

最后我们做一个简单的testing,来验证一下上面的结论。

//声明Son、Father
@interface Father : NSObject
@end
@implementation Father
@end
@interface Son : Father
@end
@implementation Son
@end

// 测试
- (void)test {
    Son *obj1 = [[Son alloc] init];
    //对象的isa
    NSLog(@"%@,%p,%p,%d,%d", obj1.class, obj1.class, Son.class, obj1.class == Son.class, class_isMetaClass(obj1.class));
    //class的isa = meta
    Class class1Isa = object_getClass(obj1.class);
    NSLog(@"%@,%p,%d", class1Isa, class1Isa, class_isMetaClass(class1Isa));
    //class的isa的isa = meta的isa = NSObject meta
    Class meta1Isa = object_getClass(class1Isa);
    NSLog(@"%@,%p,%d", meta1Isa, meta1Isa, class_isMetaClass(meta1Isa));

    Father *obj2 = [[Father alloc] init];
    //对象的isa
    NSLog(@"%@,%p,%p,%d,%d", obj2.class, obj2.class, Father.class, obj2.class == Father.class, class_isMetaClass(obj1.class));
    //class的isa = meta
    Class class2Isa = object_getClass(obj2.class);
    NSLog(@"%@,%p,%d", class2Isa, class2Isa, class_isMetaClass(class2Isa));
    //class的isa的isa = meta的isa = NSObject meta
    Class meta2Isa = object_getClass(class2Isa);
    NSLog(@"%@,%p,%d", meta2Isa, meta2Isa, class_isMetaClass(meta2Isa));
}

运行后,输出以下日志。

//obj->isa = class
Son,0x1052a8318,0x1052a8318,1,0
//class->isa = meta
Son,0x1052a82f0,1
//meta->isa = NSObject meta
NSObject,0x7fff89d23d18,1

//同上
Father,0x1052a82c8,0x1052a82c8,1,0
Father,0x1052a82a0,1
NSObject,0x7fff89d23d18,1

可以很清楚的看到:

  1. obj1->isa = Son class,即0x10a079310,且不是 meta
  2. class->isa = meta,即0x1052a82f0
  3. meta->isa = NSObject meta = Father meta->isa,即0x7fff89d23d18

参考链接

opensource objc4

iOS刨根问底-深入理解RunLoop

Tags: objc
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章