2020-02-06 11:33

探究对象与isa

摘要

:本篇主要探究对象的本质以及对象和isa的关系

对象的探究

在OC中,我们常见到以下写法

Test *t = [Test alloc];

一个类调用alloc方法,得到的返回值就是一个类的指针,也就是一个对象。那么,我们只要探究alloc方法的返回值,就可以探究类的本质

那么接下来我们就前往objc源码一探究竟。

源码地址

1.源码探究

经过不停的追溯,我们可以发现,alloc的返回值就是一个名为 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;
    }
}

那通过源码,可以看出,整个过程就是一个isa的初始化并且返回isa的值。

代码从上往下看,首先判断是不是 nonpointer(非纯指针)的isa,如果是 nonpointer 的isa,则将cls赋值给isa的cls。当然,实际情况中更多的是 nonpointer isa,继续向下看,SUPPORT_INDEXED_ISA 这个宏的值为0,那么,也就是说,initIsa 最主要做的操作,就是else中的那一部分。

那这个else主要做了些啥呢。从表面上看就是给isa进行初始赋值,那我们就继续看看 isa 到底是个什么。

通过代码追溯,我们可以看到 isa 是 isa_t 类型的,以下是isa_t的代码

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

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

可以看到,这个isa_t是个联合体,联合体中数据共用一块内存。可以看到 ISA_BITFIELD 中定义了以下内容

#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_BITFIELD \
      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

其中 shiftcls 即为存储的类的指针

2.isa与类的关系

iOS的开发者有个常用的api叫 object_getClass ,我们可以看一下,系统是如何拿到这个类的。点击源码可以看见如下代码

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
inline Class 
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();

    uintptr_t ptr = (uintptr_t)this;
    if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        return objc_tag_classes[slot];
    }
}
inline Class 
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}

可以看到,如果是 nonpointer,就返回 isa 的 bits,如果不是 nonpointer isa,则是用 bits &运算一个Mask(掩码)返回这个值

那么isa就是一个对象的信息存储,其中类的信息也包含在isa中

我们可以用lldb去验证一下

image

总结

isa是对象的一个属性,它包含了包括对象的类等一些关于对象的信息