isa_t

isa_t作用

在OC的类结构中出现的第一个成员变量就是联合体isa_t

struct objc_object {
private:
    isa_t isa;
//在早期的版本中isa只是一个Class类型的指针
//Class _Nonnull isa;

public:
        // ISA() assumes this is NOT a tagged pointer object
    Class ISA();
    ...
}

在早期的32bit版本中isa就是一个单一的指针,用于存储当前对象的类或者类的元类. 但是在64bit为操作系统上,用一个8字节指针的长度只存储一个对象地址显然是浪费的(操作系统只有一部分地址是可用于存储对象地址的空间),所以apple对这个isa指针进行了优化.

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
};

作为过渡也为了兼容早期的实现版本,这个结构中保存了变量Class class的实现,同时增加了uintptr_t(unsigned long)类型的变量bits,但由于使用的是联合体(公用体,共用变量空间),所以该结构只占用一个指针的空间.当使用bits变量进行存储时,利用位域结构将变量的各个位进行拆分赋予不同的含义,充分利用了内存空间.

利用位域使得变量内不仅仅保存了指针值,同时还保存了很多有用的信息.

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

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   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
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

以主流的arm64为例,主要包含了:

  • nonpointer:占用1bit,标识是否开启isa优化.如果是一个指针值该位为0,则表示当前结构的值只是一个指针没有保存其他信息;如果为1,则表示当前结构不是指针,而是一个包含了其他信息的位域结构;
  • has_assoc:当前对象是否使用objc_setAssociatedObject动态绑定了额外的属性;
  • has_cxx_dtor: 是否含有C++或者OC的析构函数,不包含析构函数时对象释放速度会更快;
  • shiftcls:   这个值相当于早期实现中的isa指针,是真实的指针值,在arm64处理器上只占据33位,可见其实在内存中可以用来存储对象指针的空间是很有限的;
  • magic:用于判断对象是否已经完成了初始化,在 arm64 中 0x16 是调试器判断当前对象是真的对象还是没有初始化的空间(在 x86_64 中该值为 0x3b);
  • weakly_referenced:是否是弱引用对象;
  • deallocating:对象是否正在执行析构函数(是否在释放内存);
  • has_sidetable_rc:判断是否需要用sidetable去处理引用计数;
  • extra_rc:存储该对象的引用计数值减一后的结果. 当对象的引用计数使用extra_rc足以存储时has_sidetable_rc=0;当对象的引用计数使用extra_rc不能存储时has_sidetable_rc=1.可见对象的引用计数主要存储在两个地方:如果isa中extra_rc足以存储则存储在isa的位域中;如果isa位域不足以存储,就会使用sidetable去存储.

isa_t初始化

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);


        /*
            此时省略了SUPPORT_INDEXED_ISA预编译实现,这部分可以忽略,iOS中不使用这一技术
        
        */
        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;


        // 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.bits = ISA_MAGIC_VALUE;首先使用ISA_MAGIC_VALUE将bits进行初始化,主要有两个作用:
    • 将nonpointer标记为1,证明开启了isa优化;
    • 初始化magic位,用于标记该对象已经初始化.
  • newisa.has_cxx_dtor = hasCxxDtor;初始化对象标记是否有C++或者OC析构函数,如果存在在对象释放时需要消耗更多时间来对对象进行析构;
  • newisa.shiftcls = (uintptr_t)cls >> 3;将对象指向的类或者类的元类指针赋值给位域shiftcls,因为前三位被nonpointer,has_assoc和has_cxx_dtor被占据,所以需要将真实的指针左移三位进行复制保存.

ISA_MASK

在iOS 64bit的操作系统中,为了屏蔽外界对于isa指针的直接获取对外部隐藏了真实的isa指针的值,而是需要使用ISA_MASK进行与操作来获取到真实的isa.

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
}

所以如果想要通过获取到对象真实的isa指针指向需要与ISA_MASK进行与操作:

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# else
#   error unknown architecture for packed isa
# endif


NSObject *obj = [[NSObject alloc] init];
void *ptr = (__bridge void *)obj;//墙纸转化为指针
void *isa = *(void **)ptr; //获取类中第一个成员变量isa_t值
isa = (void *)((uintptr_t)isa & ISA_MASK); //获取到isa真实的指针地址
id isaObj = (__bridge id)isa;//强转为id,即可获取到isa指向的真实对象
NSLog(@"isaObj == %@", isaObj);

该过程也可以通过LLDB实现:

(lldb) po (void *)obj
<NSObject: 0x10110f400>

(lldb) x/2g 0x10110f400
0x10110f400: 0x001d800100b38141 0x0000000000000000
(lldb) p/x (0x001d800100b38141 & ISA_MASK) //根据需要替换ISA_MASK
(unsigned long long) $1 = 0x0000000100b38140
(lldb) po $1
NSObject

ISA_MAGIC_MASK

从isa_t位域的定义中可以知道,magic变量占据了6bit的空间,使用ISA_MAGIC_MASK就可以快速地从isa_t中获取到magic值.

__arm64__中,位域magic占据了从37位(1(nonpointer)+1(has_assoc)+1(has_cxx_dtor)+33(shiftcls) )开始的6bit空间,而ISA_MAGIC_MASK=0x000003f000000001ULL,除第37-42位和最低位外所有位均为0;在__x86_64__中,位域magic占据了从47位(1(nonpointer)+1(has_assoc)+1(has_cxx_dtor)+44(shiftcls))开始的6bit空间,而ISA_MAGIC_MASK=0x001f800000000001ULL,除第44-49位和最低位外其余位均为0.所以使用isa.bits & ISA_MAGIC_MASK,可以快速判断出是否开启nonpointer优化和magic的值.

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define MAGIC_MASK      36
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define MAGIC_MASK      47
# else
#   error unknown architecture for packed isa
# endif

NSObject *obj = [[NSObject alloc] init];
void *ptr = (__bridge void *)obj;
ptr = *(void **)ptr;
uintptr_t magic = (uintptr_t)(void *)((uintptr_t)ptr & ISA_MAGIC_MASK);
if (magic & 1) {
    NSLog(@"magic == %lu", magic >> MAGIC_MASK);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值