一、基本定义
一、类对象的解析
- 类的本质:objc_class 结构体,也是一个对象
- 类对象的ISA的指针指向元类。
- 元类和类对象的名称是一样的。
- 实例对象ISA --> 类对象ISA --> 元类对象 isa --> 根元类。
一、ISA指针指向解析
1.
2.
问题:内存优化是哪部分做的?操作系统来做的吗?苹果和windows都会优化吗?
- 编译阶段通过重排成员属性的内存位置来优化内存。
- 成员变量的顺序会对内存空间造成影响吗?只有在有父类的情况会有影响,子类没办法优化父类的内存空间。
3. 影响对象内存的因素
- 成员变量
- 单个类的成员变量顺序不会影响内存大小,系统会优化
- 继承来的类,成员变量顺序会影响大小。
- 8字节对齐的方式,内存对齐在编译阶段
二、元类的继承类关系
1. 位域的定义
- 指明成员变量所占的位数。
- ISA的struct就会使用位域来定义。
struct Teacher {
char a : 7;
char b : 2;
char c : 7;
char d : 2;
}t1;
sizeof(t1) = 4;
2. 联合体的定义
- 所有成员变量共用一片内存空间;
- 每个成员变量都是可以访问这块内存;
- 空间内存的大小取决于最大的成员变量,是最大成员变量(基本数据类型)内存的整数倍。
- oc的源码当中当对于不同的平台的情况就会使用联合体。
union Teacher{
char *name;
int age;
int height;
}t1;
sizeof(t1) = 8;
union Person{
char a[7];
int b;
}p1;
sizeof(p1) = 8;
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD;
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
3. 结构体和联合体的区别
- 结构体里面的变量能够共存,联合体里面的变量互斥,优化内存。
三、内存平移的概念
1. 要点
- isa 指针和对象的关系?
- 什么时候会和具体的对象(LGPerson)关联起来?什么方法来绑定的?
- isTaggedPointer? 指针优化
- 引用计数的值存储在isa指针里面,在后期的版本,之前的版本没有。(哪个版本?)
- isa指针就是一个联合体(union)
- nonPointIsa – 8个字节 – 8 * 8 == 64bits, 存放内存地址、引用计数
2. 通过isa地址获取类对象的内存地址
define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
如下图所示,对于一个LGPerson的实例对象来说,它的内存空间以16进制打印出来前4个8字节内存空间,第一个八字节是isa的内存地址。如何通过isa的内存地址进行位运算得到实际的LGPerson的实例对象的内存地址呢? 如下图所示,在中间的值,就是实际的对象内存地址,通过将第一个8字节(ISA地址)先右移三位,把低三位清零,然后再左移12位,把左边的清零,最后再回到原来的位置,就可以得到对象具体的内存地址。 需要两边都清零,需要左移,又移来补0,清除两边的值。
四、 实例方法、属性的存储位置分析
- new = alloc + init;
- init就是一个工厂模式,NSObject 提供的一个公用接口。
五、问题
- 所有编程语言的内存空间一定是8字节的整数倍吗?
- 对象内存大小是16 的倍数是不是为了兼容优化过isa的对象和没优化过的对象,以前前8位isa只存类信息,后面8位存了现在的引用计数这些信息? 不是的,跟内存读取大小有关系。
六、用到的指令
LLDB打印指令:
p :详细数据po :稍微少一些信息x/4gx : 以16进制的形式打印4个八字节的内存空间p/x : 以16进制的形式打印
七、其它
- Link-Time Optimization: 编译器优化可能导致断点打不了。
|