iOS 底层原理, isa结构分析

603 阅读5分钟

分析 obj->initInstanceIsa(cls, hasCxxDtor);

    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

对象本质 struct

我们知道 OC 使用 clang 作为编译前端 参考⑦,这里我们可以直接看下编译产物。

  • xcode 中构建是直接生成了.o 文件。这里我们把 main.m 编译成 c++ 文件
  • 把目标文件编译成c++文件
clang -rewrite-objc main.m -o main.cpp
Clang 的主要工作:参考⑦
  • 预处理: 比如把宏嵌入到对应的位置,头文件的导入,去除注释( clang -E main.m )
  • 词法分析: 这里会把代码切成一个个 Token,比如大小括号,等于号还有字符串等
  • 语法分析: 验证语法是否正确
  • 生成 AST : 将所有节点组成抽象语法树 AST
  • 静态分析:分析代码是否存在问题,给出错误信息和修复方案
  • 生成 LLVM IR: CodeGen 会负责将语法树自顶向下遍历逐步翻译成 LLVM IR

所以这里我们可以直接搜索 我们所创建的类内容

这里我们就验证了,oc 对象是结构体

联合体位域 参考

  • 联合体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的联合体,但是任何时候只能有一个成员带有值。联合体提供了一种使用相同的内存位置的有效方式。

  • 联合体(union)中是各变量是“互斥”的

经源码查找我们查到 ISA 源码定义如下

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {
    	// 创建 - 
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {
        return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}
inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
    	// 这里创建 ISA
        isa = isa_t((uintptr_t)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 8字节 64 位 参考 ② ③ ④ ⑤
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    // uintptr_t 
    uintptr_t bits;
#if defined(ISA_BITFIELD)
	// 这里是定义的结构体
    struct {
        // ISA_BITFIELD;  // defined in isa.h
      # if __arm64__
	  #   define ISA_MASK        0x0000000ffffffff8ULL
	  #   define ISA_MAGIC_MASK  0x000003f000000001ULL
	  #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
	  #   define ISA_BITFIELD                                                      \
      # 表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等
      uintptr_t nonpointer        : 1;                                       \
      # 关联对象标志位,0没有,1存在
      uintptr_t has_assoc         : 1;                                       \
      # 该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
      uintptr_t has_cxx_dtor      : 1;                                       \
      # 存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      # 用于调试器判断当前对象是真的对象还是没有初始化的空间
      uintptr_t magic             : 6;                                       \
      # 标志对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。
      uintptr_t weakly_referenced : 1;                                       \
      # 标志对象是否正在释放内存
      uintptr_t deallocating      : 1;                                       \
      # 当对象引用技术大于 10 时,则需要借用该变量存储进位
      uintptr_t has_sidetable_rc  : 1;                                       \
      # 当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)
    };
#endif
};

验证以上内容 => 源码分析

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)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;
    }
}

关联类信息 newisa.shiftcls = (uintptr_t)cls >> 3;

  • 掩码:如果要对一个整数中的某些位进行操作,怎样表示这些位在整数中的位置呢?可以用掩码(Mask)来表示。比如掩码0x0000ff00表示对一个32位整数的8~15位进行操作 参考