【Objective -C 】OBJC2 Runtime 之isa_t(一)

引入


众所周知Objective-C 属于一门动态语言,那什么是动态语言呢?

什么是runtime


动态语言就是在运行时来执行静态语言的编译链接的工作。这就要求除了编译器之外还要有一种运行时(runtime)系统来执行编译等功能,意味着要通过runtime来实现动态创建类对象及实例对象,及消息的传递及转发。runtime基本是由c和汇编来实现。源码地址

objc_object 与 objc_class:

----------objc2:---------------
struct objc_object {
    isa_t isa;
};

struct objc_class : objc_object {
    isa_t isa;
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
};
----------objc(旧):------------
struct objc_object {
    Class isa ;
};

struct objc_class {
    Class isa ; 
    Class super_class ;
    const char * name ;
    long version ;
    long info ;
    long instance_size ;
    struct objc_ivar_list * ivars ;
    struct objc_method_list ** methodLists         
    struct objc_cache * cache ;
    struct objc_protocol_list * protocols ; 
} ;
差异:
  • 类型上我们可以新版的objc_class是继承自objc_object,可以理解为类对象,这也更好的说明了万事万物皆对象。
  • 旧版的实例对象中isa是指向他的类 Class isa ,新版isa替换成isa_t,接下来会介绍
  • 旧版的methodLists,cache,protocols,新版将有bits来完成
实现:

实例方法被调用时,会通过其持有 isa _t指针shiftcls寻找对应的类,然后在其中的 class_data_bits_t (存储调用类的方法,属性,协议等信息的地方)中查找对应的方法,如果已经缓存过cache 则从cache中寻找。我们先来介绍下isa_t及他的初始化实现。


isa_t

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { } 
    Class cls;
    uintptr_t bits; 

#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        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 deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
#       define RC_ONE   (1ULL<<45)
#       define RC_HALF  (1ULL<<18)
    }; 
}; 

union(联合体): 其中成员变量共用同一块地址空间,也就是说同一个内存段可以用来存放几种不同类型的成员,需要注意的是在每一瞬间只能存放其中的一种,而不是同时存放几种。

isa_t 结构体参数:

isa_t() : 提供一个方法 isa_t()来返回类指针

结构体参数

nonpointer : 代表是否开启NONPOINTER isa指针优化,之前的版本又叫index的其实一个意思苹果后来给这类优化方案起了名字NONPOINTER
has_assoc: 对象是否含有关联引用
has_cxx_dtor: 对象是否含有 C++ 或者 Objc 的析构器
shiftcls: 类的指针arm64下3bits,x86_64下44bits
magic: 判断对象是否初始化完成 arm下0x16 ,x86_64下 0x3b
weakly_referenced: 是否为弱引用的对象
deallocating:对象是否正在执行析构函数(是否在释放内存)
has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
extra_rc:存储该对象的引用计数值减一后的结果

我们先看一下 isa_t的init初始化方法可能会容易理解一些(以arm64做例子):

#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{  
    if (!nonpointer) {
        isa.cls = cls;
    } else { 
        isa_t newisa(0); 
        newisa.bits = ISA_MAGIC_VALUE; 
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3; 
   //在某些情况下,这个写入必须在一个单独的存储中执行
   // (例如,当实现一个类时,因为其他线程可能同时尝试使用该类)。
   // fixme在这里使用atomics来保证单存储和保证内存顺序w.r.t.类索引表…但不要太原子化,
   // 因为不想破坏实例化
        isa = newisa;
    }
}

TaggedPointernonpointer可以了解下这里先不讲了。默认objc2.0默认走 nonpointer

newisa.bits = ISA_MAGIC_VALUE

执行init时,先给 bits赋了初值 ISA_MAGIC_VALUE

#   define ISA_MAGIC_VALUE 0x000001a000000001ULL

对照isa_t里结构体,转换为二进制(不足补齐至 64 位)我们可以看到下图:


ISA_MAGIC_VALUE 二进制

我们可以看到一下变化:

  • nonpointer: 1 上面说过nonpointer决定了新老板本的替代,如果1开启,0则会使用老版本直接返回isa 指向的Class
  • magic: 010110 -> 0x16 arm64中0x16是调试器判断当前对象是真的对象还是没有初始化的空间

newisa.has_cxx_dtor = hasCxxDtor;

hasCxxDtor 表示当前对象有 C++ 或者 ObjC 的析构函数,如果没有析构函数就会快速释放内存

newisa.shiftcls = (uintptr_t)cls >> 3;

作用是优化内存,iOS 中基本数据类型也好,指针也好需要按照他门自己的内存空间按照(8bits不同系统参数不一,也可设定)采取字节对齐。我们从图中看出后三位是没有意义的反而 浪费内存随意 shiftcls 右移三位清除减小内存的消耗。
以上便是isa_t的基本知识以及之前isa 和现在版本isa_t的区别

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。