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
};
- nonpointer 标记是否是nonpointerisa 如果不是就单纯的指向类 如果是就还有下边的一些信息 0 是 纯的isa。1 是nonpointerisa
- has_assoc 是否有关联对象
- has_cxx_dtor 判断是否有c++或者Objc的析构器。如果没有可以快速释放
- shiftcls 开启指针优化 存储类指针
- magic 用于调试判断当前对象时真的对象还是没有初始化的空间
- weakly_referenced 标记当前对象是否被指向或者曾经指向一个弱引用对象 如果没有可以尽快释放
- deallocating 是否正在释放
- has_sidetable_rc 标记当前有没有sidetable 如果没有可以更快的释放
- extra_rc 表示当前对象的引用计数 如果小于10 就存储在这里 如果大于10 情况变得复杂 要往sideTable中放
isa 指向总结
很经典的那张图 不再放了 这里语言总结一下
- isa: 类对象的isa指向 类 类的isa指向元类 元类的isa指向跟元类 根源类的isa指向自己
- 父子关系 子类继承父类 父类继承跟类。子元类继承父元类父元类继承跟元类 跟元类继承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();
}
}
- 第一步创建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
- 执行一下 命令
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 - 再来看 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
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 中找到 类方法 就在元类中。