前几篇文章我们讲到,实例对象的isa指向了类,那么类的isa又指向了谁呢
我们通过LLDB调试一下一探究竟
16000710050301.jpg
我们发现类的isa为FYPerson,那么这个FYPerson是什么呢?由此我们引出一个概念: 元类
元类
类本身也是一个对象,类对象的isa的位域的内容为元类
- 元类的定义和创建都是由编译器完成的,类的归属即为元类
- 元类本身是没有名称的,由于与类相关联,所以使用了同类名一样的名称
从上面的打印过程可以总结为:
-
对象
的isa
指向类
(也可称为类对象) -
类
的isa
指向元类
-
元类
的isa
指向根元类
,即NSObject
-
根元类
的isa
指向它自己
元类内存中只存在存在一份根元类NSObject,根元类的元类是指向它自己.
这个过程也是我们经常看到的著名的一张图
isa流程图.pngisa走位
isa的走向有以下几点说明:
-
实例对象(Instance of Subclass)
的isa
指向类(class)
-
类对象(class)
isa
指向元类(Meta class)
-
元类(Meta class)
的isa
指向根元类(Root metal class)
-
根元类(Root metal class)
的isa
指向它自己
本身,形成闭环
,这里的根元类
就是NSObject
superclass走位
superclass(即继承关系)的走向也有以下几点说明:
-
类
之间 的继承
关系:-
类(subClass)
继承自父类(superClass)
-
父类(superClass)
继承自根类(RootClass)
,此时的根类是指NSObject
-
根类
继承自nil
,所以根类
即NSObject
可以理解为万物起源
,即无中生有
-
-
元类
也存在继承
,元类之间的继承关系如下:-
子类的元类(metal SubClass)
继承自父类的元类(metal SuperClass)
-
父类的元类(metal SuperClass)
继承自根元类(Root metal Class
-
根元类(Root metal Class)
继承于根类(Root class)
,此时的根类是指NSObject
-
-
【注意】
实例对象
之间没有继承关系
,类
之间有继承关系
objc_class & objc_object
我们接着看一下类编译成C++的代码
16000731359950.jpgobjc_object
是什么呢?
我们看看底层objc4的源码:
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
...
};
总结
可以看到,objc_class也是集成自objc_object,所有的对象都是基于objc_object结构体为模板创建出来的,所以所有的对象都有isa,这也侧面证明了万物皆对象的说法
类结构分析
我们补充一下内存偏移
从打印结果我们看出a和b的地址相差了4个字节,p1和p2相差了8个字节
- p1 和p2 是指针,指向了[CJLPerson alloc]创建出来的内存地址
- &p1和&p2是指针的地址,也就是二级指针
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]);
NSLog(@"%p -- %p - %p", d, d+1, d+2);
for (int i = 0; i < 4; i++) {
NSLog(@"%d ", *(d+i));
}
可以通过 首地址+偏移量
取出数组中的其他元素,其中偏移量是数组的下标,内存中首地址实际移动的字节数 等于 偏移量 * 数据类型字节数
操作符的作用:*取值 &取址
(lldb) p/x FYPerson.class
(Class) $0 = 0x00000001000022d8 FYPerson
(lldb) p (class_data_bits_t *)0x00000001000022f8
(class_data_bits_t *) $1 = 0x00000001000022f8
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000102148b40
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = 4294975608
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
(lldb) p $3.ro()
(const class_ro_t *) $4 = 0x0000000100002078
(lldb) p *$4
(const class_ro_t) $5 = {
flags = 388
instanceStart = 8
instanceSize = 32
reserved = 0
ivarLayout = 0x0000000100000f1b "\x03"
name = 0x0000000100000f12 "FYPerson"
baseMethodList = 0x00000001000020c0
baseProtocols = 0x0000000000000000
ivars = 0x0000000100002170
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000021d8
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x0000000100002170
(lldb) p *$6
(const ivar_list_t) $7 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 3
first = {
offset = 0x0000000100002298
name = 0x0000000100000f27 "enName"
type = 0x0000000100000f79 "@\"NSString\""
alignment_raw = 3
size = 8
}
}
}
(lldb) p $7.get(0)
(ivar_t) $8 = {
offset = 0x0000000100002298
name = 0x0000000100000f27 "enName"
type = 0x0000000100000f79 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
offset = 0x00000001000022a0
name = 0x0000000100000f2e "_name"
type = 0x0000000100000f79 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
offset = 0x00000001000022a8
name = 0x0000000100000f34 "_nickName"
type = 0x0000000100000f79 "@\"NSString\""
alignment_raw = 3
size = 8
}
这些就是一些指针和值的取值取址,大家自己玩一玩就好
总结:
属性存在,方法,成员变量存在于bit,ivars里面不仅存的是实例属性,还会给属性生成_name
这个实例变量
网友评论