我们知道,一个实例对象的内存空间大小跟其对应类中的成员变量有关。 我们看下这个代码:
@interface XXPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) short gender;
@end
XXPerson *p = [XXPerson alloc];
p.name = @"zhangsan";
p.age = 20;
p.height = 1.8;
p.gender = 2;
NSLog(@"%lu", malloc_size((__bridge const void *)(p)));
我们自己先按照字节对齐的方式计算:
isa:[0, 7]
name:[8, 15]
age:[16, 19]
height:[24, 31] 因为20不能被8整除,要从24开始
gender:[32, 33]
那么,按照系统的16字节对齐方,应该分配内存为48个字节,这是我们自己计算的结果,是否正确呢?来验证一下,运行看打印:
2022-04-24 15:09:13.906234+0800 SXObjcDebug[3829:94064] 32
为何是32呢😳
查查看!
看上图说话:
其中,第一个8字节0x011d8001000082ed,表示的是isa。
对象p的name和gender两个属性被放到了同一个8字节的单元中,因为它俩占用的空间小。
除去isa,表示name的8字节0x0000000100004010被排到了第二位,而name这个属性,无论是声明还是赋值,都是排第一的。
综上可得,系统在给对象分配内存空间时自动做了优化!
侦查isa !!!
上面说过,p对象的第一个8字节,表示的是isa指针信息,那么isa到底是啥呢?
我们知道, 在创建对象的过程中,会走到一个叫做_class_createInstanceFromZone的方法,而在这个方法里会调用obj->initIsa(cls);跳进去发现,isa的本质是个isa_t联合体。
struct {
ISA_BITFIELD; // defined in isa.h
};
isa的信息基本都存在这个结构体中,以__x86_64__结构为例,我们来看一下ISA_BITFIELD里面都有啥。
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44;
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
isa有8个字节,也就是8*8=64bit,其中,uintptr_t shiftcls的44位bit表示的就是对象的类所对应的内存地址。
这64位,可以分成三个部分:
3表示前三个:1+1+1;
44表示uintptr_t shiftcls;
17表示其它:6+1+1+1+8。
问题:怎么获取该对象对应的类的内存地址?
因为我们已经知道,这uintptr_t shiftcls的44位表示对象对应的类的内存地址,所以使用isa的地址右移3位,再左移20(17+3)位,再右移17位即可。
以上文中p对象为例:
通过这个方法获取类的地址稍微有点儿麻烦,还有个简单的方法。我们看到上面有个ISA_MASK,做个“&”操作即可:
此外,ISA_BITFIELD中的uintptr_t extra_rc表示的是对象的引用计数的值,也就是说,这64位的前8位表示的就是引用计数值,可直接通过位移的方式获取这个值,右移64-8位:
到此,我们了解到,iOS对象从alloc开始,先去创建一个对象,其中系统是按照16字节对齐的方式来为其分配内存空间的;之后,再创建isa指针将对象和类关联上。 iOS对象的底层探索先告一段落,以后有想到的地方再补充。