从objc源码看世界--isa初始化

1,196 阅读3分钟

1、构建实例

id objc_constructInstance(Class cls, void *bytes) 
{
    if (!cls  ||  !bytes) return nil;
    id obj = (id)bytes;

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer(); //标记类isa是否支持指针优化.
    
    if (fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        obj->initIsa(cls);
    }

    if (hasCxxCtor) {
        return object_cxxConstructFromClass(obj, cls);
    } else {
        return obj;
    }
}

在构造一个实例的时候,我们可以看到isa被初始化的影子。其中有 initInstanceIsainitIsa 两个分支,这个分支靠fast=cls->canAllocNonpointer()进行判断。接下来我们去canAllocNonpointer()中看看做了什么事。

2、判断canAllocNonpointer

bool canAllocNonpointer() {
        assert(!isFuture());
        return !instancesRequireRawIsa();
}
bool instancesRequireRawIsa() {
        return bits.instancesRequireRawIsa();
    }
#elif SUPPORT_NONPOINTER_ISA
    bool instancesRequireRawIsa() {
        return data()->flags & RW_REQUIRES_RAW_ISA;
    }
    void setInstancesRequireRawIsa() {
        data()->setFlags(RW_REQUIRES_RAW_ISA);
    }
#else
    bool instancesRequireRawIsa() {
        return true;
    }
    void setInstancesRequireRawIsa() {
        // nothing
    }
#endif

从字面理解为实例是否需要一个全新的未处理过的isa. 这个用一个 data()->flags这样一个标记位和 RW_REQUIRES_RAW_ISA 掩码进行一个与运算得到结果。这里可以理解成一个懒加载的过程,如果这个实例的类没有被实现则会实现,然后设置data()->flags标记位。(这里我们可以全局搜索setInstancesRequireRawIsa发现存在于一个名叫realizeClass的方法之中)

那这个data()是个什么玩意,我们接着往下看

class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ...

data()是类对象(objc_class)的bitsclass_data_bits_t)属性中的bitsuintptr_t)和FAST_DATA_MASK进行位运算然后转成class_rw_tclass_rw_t结构如下(省略了一些方法)

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
    ...

3、isa初始化

判断好fast,接着我们回过头来看initInstanceIsainitIsa两个方法的具体实现。

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!cls->instancesRequireRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

initInstanceIsa其实最终还是调用的initIsa

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE; // 设置一个魔力数,相当于初始化,为什么是这个数
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor; 
        newisa.shiftcls = (uintptr_t)cls >> 3; // 这部是关键
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

分支的判断中nonpointer起到决定作用,这个值是从外部传进来的。

  • 如果不需要非nonpointer,直接走isa.cls = cls(也就是说fast模式,!raw isa,canAllocNonPointer,相当于类不是第一次实现,作一个简单的指向即可) 下面整个else逻辑:

SUPPORT_INDEXED_ISA 的值是根据__ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)来设置的。表示手表或者较低的机型。现在设备较新,所以我们看一下SUPPORT_INDEXED_ISA = 0里面的情况,其中比较重要的一步就是

newisa.shiftcls = (uintptr_t)cls >> 3

isa是一个联合体,shiftcls是联合体位域中的其中其中44位(不同架构不一样,代码常规64位展示)。我们把isa的宏展开得到:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        uintptr_t nonpointer        : 1;                                         \
        uintptr_t has_assoc         : 1;                                         \
        uintptr_t has_cxx_dtor      : 1;                                         \
        uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
        uintptr_t magic             : 6;                                         \
        uintptr_t weakly_referenced : 1;                                         \
        uintptr_t deallocating      : 1;                                         \
        uintptr_t has_sidetable_rc  : 1;                                         \
        uintptr_t extra_rc          : 8;  // defined in isa.h
    };
#endif
};

把cls中bits右移三位赋值给newsia.shiftcls,不取nonpointerhas_assoc,has_css_dtor,按着位来取。

最后isa=newisa返回出去,这个就是isa初始化的一个简单过程。

4、流程图
isa.jpeg