前言
在前面我们学习到,在alloc的流程中,有一步操作就是初始化isa,那isa和ios中的对象是怎么的关系存储呢?他的主要作用是什么呢?下面开始分析
isa 所属的结构体
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// initIsa() should be used to init the isa of new objects only.
// If this object already has an isa, use changeIsa() for correctness.
// initInstanceIsa(): objects with no custom RR/AWZ
// initClassIsa(): class objects
// initProtocolIsa(): protocol objects
// initIsa(): other objects
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
private:
void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
};
可以看出,objc_object结构体才是存储isa具体信息,从它的初始化方法也可以看出,必须由个Class跟其绑定。而这个Class,就是我们要讨论的isa的指向问题。
那objc_object 跟NSObject有什么关系呢?我们看下object的源码
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
///// objc_class 结构
typedef struct objc_class *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
.....
}
///// objc_object 结构
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
....
}
///// isa_t 结构
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
};
可以看出NSObject-> objc_class -> objc_object ->isa_t应该是这么一个关系
isa指向的分析
我们可以用下面测试:
通过源码我们可以看到isa的获取方式
return (Class)(isa.bits & ISA_MASK);
define ISA_MASK 0x00007ffffffffff8ULL
我们在代码中进行测试下
对象的isa指向
LGPerson *p = [LGPerson alloc];
// 看看打印信息
p/x LGPerson.class
(Class) $13 = 0x0000000100002420 LGPerson // 拿到Class
// 拿到p的信息,第一个为isa的值
(lldb) x/4gx p
0x10062f7b0: 0x001d800100002421 0x0000000000000000
0x10062f7c0: 0x00007fff304944a6 0x0000000100372da9
// 用isa的值,进行遮罩取值,发现拿到的就是 LGPerson.class
(lldb) p/x 0x001d800100002421 & 0x00007ffffffffff8ULL
(unsigned long long) $15 = 0x0000000100002420
可以看出来isa里面存储了类信息。我们可以看出来,对象的isa指向了类,类里面也有个isa,那它指向谁呢? 代码验证下
类的isa指向
// 打印出类对象地址信息
(lldb) x/4gx LGPerson.class
0x100002420: 0x001d8001000023f9 0x00007fff912c7140
0x100002430: 0x000000010073af90 0x000000070000000f
// 看类对象的isa指向谁
(lldb) p/x 0x001d8001000023f9 & 0x00007ffffffffff8ULL
(unsigned long long) $20 = 0x00000001000023f8
(lldb) po 0x00000001000023f8
LGPerson
通过信息可以看出,对象的isa也指向了LGPerson,而这个LGPerson,其实是元类,那元类的isa又指向谁呢?接着验证
元类的isa指向
// 获取元类地址信息
(lldb) x/4gx 0x00000001000023f8
0x1000023f8: 0x001dffff912c70f1 0x00007fff912c70f0
0x100002408: 0x00000001006357c0 0x0000000400000007
// 获取元类isa 指向
(lldb) p/x 0x001dffff912c70f1 & 0x00007ffffffffff8ULL
(unsigned long long) $22 = 0x00007fff912c70f0
(lldb) po 0x00007fff912c70f0
NSObject
通过上面可以看出元类的isa指向了NSObject,而这就是根元类,那么根元类的isa又指向谁呢?
根元类的isa指向
// 获取根元类的地址信息
(lldb) x/4gx 0x00007fff912c70f0
0x7fff912c70f0: 0x001dffff912c70f1 0x00007fff912c7140
0x7fff912c7100: 0x0000000100537a10 0x0000000300000007
// 获取跟元类isa 指向
(lldb) p/x 0x001dffff912c70f1 & 0x00007ffffffffff8ULL
(unsigned long long) $24 = 0x00007fff912c70f0
(lldb)
发现跟元类的isa指向的地址0x00007fff912c70f0 和 0x00007fff912c70f0是同一个地址,也就是说根元类的isa指向自己。 我们可以看下面这张图
总结
