iOS之isa的底层探索一

520 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

在探索alloc底层的时候,我们发现initInstanceIsa函数是其核心方法之一,它负责将class与isa进行关联,所以我们来探索一下initInstanceIsa的源码

inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { 
    ASSERT(!cls->instancesRequireRawIsa()); 
    ASSERT(hasCxxDtor == cls->hasCxxDtor()); 
    
    initIsa(cls, true, hasCxxDtor); 
}

继续探索initIsa函数

inline void objc_object::initIsa(Class cls, bool nonpointer, >UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor) { 
    ASSERT(!isTaggedPointer());
    
    isa_t newisa(0);
    
    if (!nonpointer) { 
        newisa.setClass(cls, this);
    } else { 
        ASSERT(!DisableNonpointerIsa); 
        ASSERT(!cls->instancesRequireRawIsa()); 
    
#if SUPPORT_INDEXED_ISA 
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE; 

        newisa.has_cxx_dtor = hasCxxDtor; 
        newisa.indexcls = (uintptr_t)cls->classArrayIndex(); 
    
#else
        newisa.bits = ISA_MAGIC_VALUE; 
    
# if ISA_HAS_CXX_DTOR_BIT 
        newisa.has_cxx_dtor = hasCxxDtor; 
# endif 
        newisa.setClass(cls, this); 
#endif 
        newisa.extra_rc = 1; 
    } 
    isa = newisa;
}

可以发现这段函数有以下内容:

  • 初始化isa_t
  • nonpointer,直接设置class
  • 否则,对bits赋值,关联类信息 找到SUPPORT_INDEXED_ISA的定义:
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
# define SUPPORT_INDEXED_ISA 1 
#else 
# define SUPPORT_INDEXED_ISA 0 
#endif
  • 条件判断,默认为 # define SUPPORT_INDEXED_ISA 1的分支

isa_t联合体

union isa_t { 
    isa_t() { } 
    isa_t(uintptr_t value) : bits(value) { } 
    
    uintptr_t bits; 
    
private: 
    Class cls; 
    
public: 
#if defined(ISA_BITFIELD) 
    struct { 
        ISA_BITFIELD; 
    }; 
    
    bool isDeallocating() { 
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    void setDeallocating() { 
        extra_rc = 0; 
        has_sidetable_rc = 0; 
    }
#endif
    
    void setClass(Class cls, objc_object *obj); 
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated); 
};

通过对isa_t的源码分析,可以发现:

  • isa_t为联合体
  • bitscls两个成员变量,它们是互斥的
  • ISA_BITFIELD:isa结构 通过对源码的阅读我们可以知道以下isa的结构
# if __arm64__ 
#    if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR 
#        define ISA_MASK 0x007ffffffffffff8ULL 
#        define ISA_MAGIC_MASK 0x0000000000000001ULL 
#        define ISA_MAGIC_VALUE 0x0000000000000001ULL 
#        define ISA_HAS_CXX_DTOR_BIT 0 
#        define ISA_BITFIELD 
          uintptr_t nonpointer : 1; 
          uintptr_t has_assoc : 1;
          uintptr_t weakly_referenced : 1; 
          uintptr_t shiftcls_and_sig : 52; 
          uintptr_t has_sidetable_rc : 1; 
          uintptr_t extra_rc : 8 
#        define RC_ONE (1ULL<<56) 
#        define RC_HALF (1ULL<<7) 
#    else 
#        define ISA_MASK 0x0000000ffffffff8ULL 
#        define ISA_MAGIC_MASK 0x000003f000000001ULL 
#        define ISA_MAGIC_VALUE 0x000001a000000001ULL 
#        define ISA_HAS_CXX_DTOR_BIT 1 
#        define ISA_BITFIELD
          uintptr_t nonpointer : 1; 
          uintptr_t has_assoc : 1; 
          uintptr_t has_cxx_dtor : 1; 
          uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ 
          uintptr_t magic : 6; 
          uintptr_t weakly_referenced : 1;
          uintptr_t unused : 1; 
          uintptr_t has_sidetable_rc : 1; 
          uintptr_t extra_rc : 19 
#        define RC_ONE (1ULL<<45) 
#        define RC_HALF (1ULL<<18) 
#    endif 

#    elif __x86_64__ 
#        define ISA_MASK 0x00007ffffffffff8ULL 
#        define ISA_MAGIC_MASK 0x001f800000000001ULL 
#        define ISA_MAGIC_VALUE 0x001d800000000001ULL 
#        define ISA_HAS_CXX_DTOR_BIT 1 
#        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 unused : 1; 
          uintptr_t has_sidetable_rc : 1; 
          uintptr_t extra_rc : 8 
#        define RC_ONE (1ULL<<56) 
#        define RC_HALF (1ULL<<7) 
#    else 
#        error unknown architecture for packed isa 
#    endif

通过对isa结构的分析发现

  • 不同CPU结构,isa结构略有差异
  • __has_feature:编译器是否支持该功能
  • ptrauth_callsarm64e架构,引入指针验证代码(PAC
    • 用于检测和防止内存中指针的意外更改
    • 支持Apple A12或更高版本A系列处理器的设备(如:XS/XS MAX/XR
    • 详情可见:官方文档
  • TARGET_OS_SIMULATOR:判断是否为模拟器

通过对isa结构的分析可以得出以下isa位图

arm64位图 image.png x86_64位图

image-2.png

  • nonpointer:表示是否对isa指针开启指针优化。0:纯isa指针,1:不止是类对象地址,isa中包含了类信息、对象的引用计数等。
  • has_assoc:关联对象标志位。0:不存在,1:存在
  • has_cxx_dtor:该对象是否有C++或者Objc的析构器。如果有析构函数,则需要做析构逻辑。如果没有,则可以更快的释放对象
  • shiftcls:存储类指针的值。开启指针优化的情况下,在arm64架构中,有33位用来存储类指针
  • magic:用于调试器判断当前对象是真的还是没有初始化的空间
  • weakly_refrenced:标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放
  • deallocating:标志对象是否正在释放内存
  • has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位
  • extra_rc:表示该对象的引用计数值,实际上是引用计数值减1.例如,如果对象的引用计数位10,那么extra_rc为9,如果引用计数大于10,则需要用到下面的has_sidetable_rc

结论:

  • isa使用联合体+位域的方式存储,优化内存空间
  • 类型分为nonpointer和非nonpointer
  • nonpointer只存储指针地址,nonpointer还存储类的其他信息