iOS isa指针初始化过程 & 类结构探索

isa 指针各位域所指代含义
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        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
    };
#endif
};
  1. nonpointer 标记是否是nonpointerisa 如果不是就单纯的指向类 如果是就还有下边的一些信息 0 是 纯的isa。1 是nonpointerisa
  2. has_assoc 是否有关联对象
  3. has_cxx_dtor 判断是否有c++或者Objc的析构器。如果没有可以快速释放
  4. shiftcls 开启指针优化 存储类指针
  5. magic 用于调试判断当前对象时真的对象还是没有初始化的空间
  6. weakly_referenced 标记当前对象是否被指向或者曾经指向一个弱引用对象 如果没有可以尽快释放
  7. deallocating 是否正在释放
  8. has_sidetable_rc 标记当前有没有sidetable 如果没有可以更快的释放
  9. extra_rc 表示当前对象的引用计数 如果小于10 就存储在这里 如果大于10 情况变得复杂 要往sideTable中放
isa 指向总结

很经典的那张图 不再放了 这里语言总结一下

  1. isa: 类对象的isa指向 类 类的isa指向元类 元类的isa指向跟元类 根源类的isa指向自己
  2. 父子关系 子类继承父类 父类继承跟类。子元类继承父元类父元类继承跟元类 跟元类继承NSObject
isa 初始化过程
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;
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
        isa = newisa;
    }
}

如果不是nonpointer 就是没有开启指针优化 直接指向类。如果开始了指针优化 isa中就存储了其他信息。给bits has_cxx_dtor 都赋上初始值

isa 验证

通过下面获取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 & 0x00007ffffffffff8);
#endif
}

创建一个继承子NSObject的类 People类 创建对象p LLDB调试

p/x p.class
(Class) $2 = 0x000000010d3085a8 People  类的内存地址
x/4gx p
0x6000004cc330: 0x000000010d3085a8 0x0000000000000000
0x6000004cc340: 0x00007f9df0405820 0x0000000000000000
p 0x000000010d3085a8 & 0x00007ffffffffff8
(long) $4 = 4516251048
p/x $4
(long) $4 = 0x000000010d3085a8
通过$2 和$4 可以发现 两个地址一摸一样 所以
p对象的isa指向People 也就是他的类 

接着打印类的isa

(lldb) x/4gx $2
0x106c28580: 0x0000000106c28558 0x00007fff89cc4580
0x106c28590: 0x00006000036cf7f0 0x0003801800000003
(lldb) p 0x0000000106c28558 & 0x00007ffffffffff8
(long) $3 = 4408378712
(lldb) po $3
People
People 类的isa还是People 其实是指向元类。 这个地方可以通过 objc_getMetaClass(<#const char * _Nonnull name#>) 进行验证

继续打印元类的isa

x/4gx $3
0x106c28558: 0x00007fff89cc4558 0x00007fff89cc4558
0x106c28568: 0x00006000036cf7a0 0x0003c03100000003
(lldb) p 0x00007fff89cc4558  & 0x00007ffffffffff8
(long) $4 = 140735505253720
(lldb) po $4
NSObject
People类的isa指向 NSObject根元类

继续探索 我们打印$4 也就是根元类的父类

po 140735505253720 + 8
<NSObject: 0x7fff89cc4560>  根元类的父类是NSObject
iOS 类探索

objc_class 源码

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
}
  1. 第一步创建People 对象 并且获取到类
people 类结构
@interface People : NSObject

@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *location;
@property(nonatomic,assign)int age;

- (void)sayGoodBy;
+ (void)sayHello;
@end

@implementation People{
    NSString *_doHomeWord;
}
- (void)sayGoodBy{
    
}
+ (void)sayHello{
    
}
@end
  1. 执行一下 命令
    People *p = [[People alloc]init];
    x/4gx p
    0x102e0d730: 0x001d800100002645 0x0000000000000000
    0x102e0d740: 0x0000000000000000 0x0000000000000000
    (lldb) p 0x001d800100002645 & 0x00007ffffffffff8
    (long) $1 = 4294977088
    (lldb) po $1
    People 此时此刻拿到类 也就是$1
  2. 再来看 cache_t
struct cache_t {
    struct bucket_t *_buckets; // 8字节
    mask_t _mask; // typedef uint32_t mask_t 4字节
    mask_t _occupied;  // 4字节

经过对cache_t 的分析
再偏移16个字节 就该拿到 class_data_bits_t 继续

  1. 打印类的内存结构 也就是 $1
    x/4gx $1
    0x100002640: 0x001d800100002619 0x0000000100b38140
    0x100002650: 0x0000000102e0d760 0x0000000100000003
    po 0x0000000100b38140
    NSObject 打印出父类
    此时 0x100002640 + 32个字节应该就是 bits 也就是 0x100002660

此时进行强转 p (class_data_bits_t *)0x0000000100002660
(class_data_bits_t *) $6 = 0x0000000100002660
p *$6
(class_data_bits_t) $7 = (bits = 4343255444) 拿到指针
p (class_rw_t *)$7.data()
(class_rw_t *) $11 = 0x0000000102e0d190。拿到class_rw_t类型的data数据
p $11->ro
(const class_ro_t *) $13 = 0x0000000100002248。拿到ro

看 class_ro_t 的源码
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

p $13->baseMethodList
(method_list_t *const) $15 = 0x00000001000020d8 拿到方法列表
就可以通过 p $15.get(index) 获取方法 这个方法是从 entsize_list_tt 结构题中获得

这里掉了一个坑 开始 sayGoodBy的实例方法没实现 所以这个列表中一直没有

p $13.baseProperties
(property_list_t *const) $36 = 0x0000000100002210
Fix-it applied, fixed expression was:
$13->baseProperties 可以拿到属性列表
p $36.get(0) 就是name

p $13.ivars
(const ivar_list_t *const) $40 = 0x0000000100002188
Fix-it applied, fixed expression was:
$13->ivars 拿到变量列表
p $40.get(0)

(ivar_t) $41 = {
  offset = 0x00000001000025d0
  name = 0x0000000100001e28 "_doHomeWord"
  type = 0x0000000100001ec7 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
  Fix-it applied, fixed expression was: 
    $40->get(0)
    就把我们的成员变量_doHomeWord拿到了 
总结

经过探索发现 OC的 类本质上是objc_class 的一个结构体 存储了 superclass cache bits 方法列表 属性列表 变量列表 都存在了 class_rw_t 的结构体中,其中类的成员变量在ivars中 属性在baseProperties 但是在ivars 中也是可以找到带 _ 的属性变量 实例方法 在baseMethodList 中找到 类方法 就在元类中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值