2019-10-13
类的成员变量列表描述类的对象需要保存哪些数据,包括这些数据的名称、类型、在对象内存空间中占用的区块、对齐方式准则信息。通过成员变量的内存布局信息,可以直接由对象内存地址定位成员变量的内存地址,实现高效的对象数据读写。
一、成员变量数据结构
成员变量的数据结构是ivar_t结构体,定义指向ivar_t结构体的指针ivar_t *类型为Ivar。ivar_t结构体的offset、size、alignment_raw共同界定了成员变量在对象内存空间中占用的区块,ivar_t包含以下成员:
offset:成员变量在对象内存空间中的偏移量;name:成员变量的名称;type:成员变量的类型编码,用字符串表示成员变量的类型,可参考苹果官方文档 Type Encodings;alignment_raw:成员变量的对齐准则,表示成员变量的对齐字节数为2^alignment_raw。例如,占用4字节的int类型成员变量alignment_raw为2,占用8字节的指针类型成员变量alignment_raw为3;size:成员变量在对象内存空间中占用的空间大小;
typedef struct ivar_t *Ivar;
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
// alignment_raw有时值为-1,需调用alignment()获取成员变量的对齐准则
uint32_t alignment_raw;
uint32_t size;
uint32_t alignment() const {
if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
return 1 << alignment_raw;
}
};
注意:默认情况下,类的成员变量对齐方式和C语言结构体的原则是一致的。例如:继承自
NSObject的SomeClass类依次包含bool、int、char、id类型的四个成员,SomeClass实例的内存图如下。注意到,bool类型的bo成员本该可以 1 bit 就可以表示但却占了4个字节的内存空间,这都是因为内存对齐原则。
类的所有成员变量保存在一个顺序表容器中,其类型为ivar_list_t结构体,代码如下。ivar_list_t继承了entsize_list_tt顺序表模板,增加了bool containsIvar(Ivar ivar) const函数,用于查找传入的Ivar类型的ivar是否包含在成员变量列表中。
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
bool containsIvar(Ivar ivar) const {
// 直接与顺序表开头地址与结尾地址比较,超出该内存区块表示不在该成员变量列表中
return (ivar >= (Ivar)&*begin() && ivar < (Ivar)&*end());
}
};
二、entsize_list_tt 模板
entsize_list_tt是 runtime 定义的一种顺序表模板。entsize_list_tt<typename Element, typename List, uint32_t FlagMask>模板中,Element表示元素的类型,List表示所定义的顺序表容器类型名称,FlagMask用于从entsize_list_tt的entsizeAndFlags获取目标数据(Flag标志 或者 元素占用空间大小)。
既然entsize_list_tt是顺序表,那么所占用内存空间必然是连续分配的。由于每个元素都是同类型,占用相同大小的内存空间,因此可以通过索引值及首元素地址来定位到具体元素的内存地址。entsize_list_tt包含三个成员:
entsizeAndFlags:entsize 是 entry size 的缩写,因此该成员保存了元素 Flag 标志 以及 元素占用空间大小,entsizeAndFlags & ~FlagMask获取元素占用空间大小,entsizeAndFlags & FlagMask获取 Flag 标志(可指定 Flag 标志用于特殊用途);count:容器的元素数量;first:保存首元素,注意是首元素,不是指向首元素的指针;
define WORD_SHIFT 3UL
// 顺序表模板,其中Element为元素类型,List为定义的顺序表容器类型, FlagMask指定entsizeAndFlags成员的最低多少位
// 可用作标志位,例如0x3表示最低两位用作标志位。
template <typename Element, typename List, uint32_t FlagMask>
struct entsize_list_tt {
uint32_t entsizeAndFlags; //入口数量及Flag标志位
uint32_t count;
Element first;
uint32_t entsize() const {
return entsizeAndFlags & ~FlagMask;
}
uint32_t flags() const {
return entsizeAndFlags & FlagMask;
}
Element& getOrEnd(uint32_t i) const {
assert(i <= count);
return *(Element *)((uint8_t *)&first + i*entsize());
}
Element& get(uint32_t i) const {
assert(i < count);
return getOrEnd(i);
}
size_t byteSize() const {
return sizeof(*this) + (count-1)*entsize();
}
List *duplicate() const {
return (List *)memdup(this, this->byteSize());
}
struct iterator;
const iterator begin() const {
return iterator(*static_cast<const List*>(this), 0);
}
iterator begin() {
return iterator(*static_cast<const List*>(this), 0);
}
const iterator end() const {
return iterator(*static_cast<const List*>(this), count);
}
iterator end() {
return iterator(*static_cast<const List*>(this), count);
}
// 内嵌的迭代器定义代码
struct iterator {
...
}
};
关于entsize_list_tt需要注意几个细节:
- 容器直接保存元素,不是指向元素的指针,
first成员也是元素; count成员是容器包含的元素数,byteSize()返回容器占用总内存字节数,sizeOf(this)返回容器的三个成员占用的字节数,具体大小取决于 Element 占用内存大小以及 Element 的对齐结构;
下面例举一个使用entsize_list_tt模板定义的,元素大小为48字节的一个容器对象占用内存空间示例。阴影区域之间是容器所占用的连续内存空间,黄色区域表示用于保存元素的内存区域,每块黄色内存区域大小均为48字节。红色标记内存地址是元素的起始地址,begin()方法返回首元素的起始地址,end()方法返回容器的结束地址。
注意:
entsize_list_tt数据结构非常重要,方法列表、分类列表、协议列表等数据结构也是使用该模板定义。
三、类的 ivar layout
Ivar layout 表示成员变量布局,描述每个成员变量在对象内存空间中占用的内存区块,成员变量布局信息分散在类的instanceStart、instanceSize以及所有ivar_t的offset、size、alignment_raw中。Ivar layout 也可以理解为广义上的成员变量布局。默认情况下,成员变量按照类型编码(type coding)进行自动内存对齐,例如,类的第一个成员变量是uint_32,则该成员变量offset为类的instanceStart,size为4,alignment_raw为2。
class_ro_t结构体中包含ivarLayout、weakIvarLayout成员,用于标记对象占用的内存空间中,哪些 WORD 有被用来存储id类型的成员变量,weakIvarLayout专门针对weak类型成员变量。ivarLayout、weakIvarLayout可以理解为狭义上的成员变量布局。objc_class用十六进制数表示类的 ivar layout,该十六进制数是通过压缩二进制 layout bitmap 获得。例如,调用class_getIvarLayout获取UIView的ivarLayout为0x0312119A12。
注意:最新版本 runtime 中简化了成员变量布局过程,
ivarLayout、weakIvarLayout的应用场景十分局限,仅在_class_lookUpIvar(...)查询类的成员变量时有比较重要的作用。
3.1 layout bitmap
Runtime 中用layout_bitmap结构体表示压缩前的ivarLayout,保存的是一个二进制数,二进制数每一位标记类的成员变量空间(instanceStart为起始instanceSize大小的内存空间)中,对应位置的 WORD 是否存储了id类型成员变量。例如,二进制数0101表示成员第二个、第四个成员变量是id类型。layout_bitmap包含以下成员:
bits:用uint8_t指针bits指向二进制数首地址;bitsAllocated:表示用于保存该二进制数而分配的内存空间大小,单位bit,由于按字节读取,因此bitsAllocated必定是8的倍数;bitCount:二进制数未必完全占满bitsAllocated大小的内存空间,因此用bitCount记录,内存空间中的有效位数;weak:标记是否用于描述weakIvarLayout;
typedef struct {
uint8_t *bits;
size_t bitCount;
size_t bitsAllocated;
bool weak;
} layout_bitmap;
设置成员变量对应的ivarLayout、weakIvarLayout位调用layout_bitmap_set_ivar(...)函数。其中bits参数为类的当前ivarLayout或weakIvarLayout;type参数成员变量的类型编码;offset为成员变量的offset。
void
layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset)
{
size_t bit = offset / sizeof(id);
if (!type) return;
if (type[0] == '@' || 0 == strcmp(type, "^@")) {
// id 或 id * 或 Block ("@?")
set_bits(bits, bit, 1);
}
else if (type[0] == '[') {
// id[]
char *t;
unsigned long count = strtoul(type+1, &t, 10);
if (t && t[0] == '@') {
set_bits(bits, bit, count);
}
}
else if (strchr(type, '@')) {
_objc_inform("warning: failing to set GC layout for '%s'\n", type);
}
}
static void set_bits(layout_bitmap bits, size_t which, size_t count)
{
size_t bit;
for (bit = which; bit < which + count && bit < bits.bitCount; bit++) {
bits.bits[bit/8] |= 1 << (bit % 8);
}
if (bit == bits.bitCount && bit < which + count) {
_objc_fatal("layout bitmap too short");
}
}
3.1.1 压缩 layout bitmap
类的ivarLayout是layout_bitmap压缩后得到的十六进制数,layout_bitmap压缩调用compress_layout(...)实现。其中bits参数指向保存layout_bitmap的内存;bitmap_bits参数为二进制数的位数;weak参数表示bits数据是否为weakIvarLayout。
static unsigned char *
compress_layout(const uint8_t *bits, size_t bitmap_bits, bool weak)
{
bool all_set = YES;
bool none_set = YES;
unsigned char *result;
// 多分配些额外的位
unsigned char * const layout = (unsigned char *)
calloc(bitmap_bits + 1, 1);
unsigned char *l = layout;
size_t i = 0;
while (i < bitmap_bits) {
// skip为本次循环二进制数连续的0位数,scan为连续的1位数
size_t skip = 0;
size_t scan = 0;
while (i < bitmap_bits) {
uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
if (bit) break;
i++;
skip++;
}
while (i < bitmap_bits) {
uint8_t bit = (uint8_t)((bits[i/8] >> (i % 8)) & 1);
if (!bit) break;
i++;
scan++;
none_set = NO;
}
// skip和scan的值均不能超过15,超过15则立即进行分割
if (skip) all_set = NO;
if (scan) none_set = NO;
while (skip > 0xf) {
*l++ = 0xf0;
skip -= 0xf;
}
if (skip || scan) {
*l = (uint8_t)(skip << 4);
while (scan > 0xf) {
*l++ |= 0x0f;
scan -= 0xf;
}
*l++ |= scan;
}
}
// 插入终止字节
*l++ = '\0';
if (none_set && weak) {
result = NULL; // NULL weak layout 表示类不包含任何weak类型成员变量
} else if (all_set && !weak) {
result = NULL; // NULL ivar layout 表示类类所有成员变量均为`id`类型
} else {
result = (unsigned char *)strdup((char *)layout);
}
free(layout);
return result;
}
总结layout_bitmap的压缩过程如下图所示:
3.1.1 解压缩 layout bitmap
调用decompress_layout(...)解压缩ivarLayout,为压缩的逆过程。例如,增加成员变量时需要更新ivarLayout,此时需要先解压ivarLayout的十六进制数得到layout_bitmap,然后更新layout_bitmap数据,最后压缩layout_bitmap得到十六进制数保存到ivarLayout。这里不做详细介绍,仅贴出相关源代码。
static void decompress_layout(const unsigned char *layout_string, layout_bitmap bits)
{
unsigned char c;
size_t bit = 0;
while ((c = *layout_string++)) {
unsigned char skip = (c & 0xf0) >> 4;
unsigned char scan = (c & 0x0f);
bit += skip;
set_bits(bits, bit, scan);
bit += scan;
}
}
3.2 non-fragile instance variables
Non-fragile instance variables 是内存布局的一个重要特征。当一个类的继承关系确立之后(类注册到内存后),理论上成员变量的内存布局在编译时是可以固定的,也就是说类的ivar_t成员变量的offset是可以确定的。但是,固定offset带来坏处是,如果依赖框架升级,某基类的对象内存布局发生变化,譬如增加了成员变量,那么依赖于该框架基类的 APP 将不得不重新编译以更新offset。
Runtime 引入了 non-fragile instance variables 计数以避免以上问题。类的成员变量offset的值在编译时不固定,而是运行时根据父类的instanceSize动态调整(具体发生在 class realizing 阶段),当然这个过程是在类注册到内存之前完成的。如果依赖框架升级,某基类的对象内存布局发生变化,只要重启APP以触发重新注册依赖于该基类的扩展类,从而更新扩展类的成员变量offset完成类的 ivar layout 动态调整过程。
All instance variables in 64-bit Objective-C are non-fragile. That is, existing compiled code that uses a class's ivars will not break when the class or a superclass changes its own ivar layout. In particular, framework classes may add new ivars without breaking subclasses compiled against a previous version of the framework.(来自 runtime 源代码 Readme)
既然类的 ivar layout 可以运行时动态调整,是不是就意味着就可以解除类注册后才能动态添加成员变量的限制呢?其实还是同样的问题,non-fragile instance variables 只是在类载入内存阶段动态调整了类的 ivar layout 结构,并没有重新构建内存中已存在的该类的实例,内存中调整ivar layout 前后所构建的实例内存布局不一致,这显然是不允许的。Non-fragile instance variables 只能保证子类在父类扩展后能正常实例化,不能保证扩展前构建的实例仍然可用.
以下是关于 non-fragile instance variables 的具体例子的图示。如图上方,SubInstance 是类的一个实例,OldSuperInstance 是类的父类的一个实例,内存布局如图。
-
左边采用成员变量编译时固定偏移,当父类增加 aa_new、bb_new 成员,由于类的 ivar layout 内存布局不会做相应变更,此时构建类的实例时,父类的 aa_new、bb_new 和类的 c_sub、d_sub 成员的内存空间重叠,导致不能正常构建类的实例,因此类不得不重新编译。直接体现为:使用了该类旧版本的应用、框架不能正常运行,需要使用重新编译后的更新了类的成员变量
offset的新版本; -
左边采用 non-fragile instance variables,当父类增加 aa_new、bb_new 成员,类的 ivar layout 及 c_sub、d_sub 成员变量的
offset做了相应的动态调整,类的实例能正常构建。直接体现为:使用了该类旧版本的应用、框架,在完成依赖类更新并重新启动应用后仍能正常运行。
类的内存布局调整的代码主要集中在reconcileInstanceVariables(...)函数中,这里只贴出关键代码,和作必要注释,不做深入讨论。
static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro_t*& ro)
{
class_rw_t *rw = cls->data();
assert(supercls); // 不处理根类,因为没有父类就不需要调整布局
assert(!cls->isMetaClass()); // 不处理元类,因为没有成员变量
// Non-fragile ivars 根据父类调整类的成员变量布局
const class_ro_t *super_ro = supercls->data()->ro;
if (DebugNonFragileIvars) {
// Non-fragile ivars 成员变量根据父类进行必要平移
// 不处理以下类: *NSCF* classes, __CF* classes, NSConstantString,
// NSSimpleCString
const char *clsname = cls->mangledName();
if (!strstr(clsname, "NSCF") &&
0 != strncmp(clsname, "__CF", 4) &&
0 != strcmp(clsname, "NSConstantString") &&
0 != strcmp(clsname, "NSSimpleCString"))
{
uint32_t oldStart = ro->instanceStart;
class_ro_t *ro_w = make_ro_writeable(rw);
ro = rw->ro;
// 找到类的alignment最大的成员变量,默认使用系统WORD字节数
uint32_t alignment = 1<<WORD_SHIFT;
if (ro->ivars) {
for (const auto& ivar : *ro->ivars) {
if (ivar.alignment() > alignment) {
alignment = ivar.alignment();
}
}
}
uint32_t misalignment = ro->instanceStart % alignment;
uint32_t delta = ro->instanceStart - misalignment;
ro_w->instanceStart = misalignment;
ro_w->instanceSize -= delta;
if (PrintIvars) {
_objc_inform("IVARS: DEBUG: forcing ivars for class '%s' "
"to slide (instanceStart %zu -> %zu)",
cls->nameForLogging(), (size_t)oldStart,
(size_t)ro->instanceStart);
}
if (ro->ivars) {
for (const auto& ivar : *ro->ivars) {
if (!ivar.offset) continue; // anonymous bitfield
*ivar.offset -= delta;
}
}
}
}
if (ro->instanceStart >= super_ro->instanceSize) {
// 扩展后的父类`instanceSize`,没有超过原先的`instanceSize`
return;
}
if (ro->instanceStart < super_ro->instanceSize) {
// 扩展后的父类`instanceSize`,超过原先的`instanceSize`,则成员变量
// 布局必须整体平移
if (PrintIvars) {
_objc_inform("IVARS: sliding ivars for class %s "
"(superclass was %u bytes, now %u)",
cls->nameForLogging(), ro->instanceStart,
super_ro->instanceSize);
}
class_ro_t *ro_w = make_ro_writeable(rw); // 将ro置为可读写
ro = rw->ro;
moveIvars(ro_w, super_ro->instanceSize); // 平移成员变量布局
gdb_objc_class_changed(cls, OBJC_CLASS_IVARS_CHANGED, ro->name); // 类变更事件,忽略
}
}
static void moveIvars(class_ro_t *ro, uint32_t superSize)
{
runtimeLock.assertLocked();
uint32_t diff;
assert(superSize > ro->instanceStart);
diff = superSize - ro->instanceStart;
if (ro->ivars) {
// 找到类的alignment最大的成员变量
uint32_t maxAlignment = 1;
for (const auto& ivar : *ro->ivars) {
if (!ivar.offset) continue;
uint32_t alignment = ivar.alignment();
if (alignment > maxAlignment) maxAlignment = alignment;
}
// 计算平移量
uint32_t alignMask = maxAlignment - 1;
diff = (diff + alignMask) & ~alignMask;
// 成员变量布局平移
for (const auto& ivar : *ro->ivars) {
if (!ivar.offset) continue;
uint32_t oldOffset = (uint32_t)*ivar.offset;
uint32_t newOffset = oldOffset + diff;
*ivar.offset = newOffset;
if (PrintIvars) {
_objc_inform("IVARS: offset %u -> %u for %s "
"(size %u, align %u)",
oldOffset, newOffset, ivar.name,
ivar.size, ivar.alignment());
}
}
}
*(uint32_t *)&ro->instanceStart += diff;
*(uint32_t *)&ro->instanceSize += diff;
}
四、成员变量的应用
4.1 查询成员变量信息
ivarLayout和weakIvarLayout可以体现在_class_lookUpIvar(...)函数代码中,_class_lookUpIvar(...)用于查询目标成员变量偏移量offset和内存管理方式。ivarLayout和weakIvarLayout用于判断成员变量的内存管理方式。ivarLayout中标记为scan的成员变量的内存管理方式为strong;weakIvarLayout中标记为scan的成员变量的内存管理方式为weak。
// 成员变量的内存管理方式,包括:未知/strong/weak/assign。
typedef enum {
objc_ivar_memoryUnknown, // unknown / unknown
objc_ivar_memoryStrong, // direct access / objc_storeStrong
objc_ivar_memoryWeak, // objc_loadWeak[Retained] / objc_storeWeak
objc_ivar_memoryUnretained // direct access / direct access
} objc_ivar_memory_management_t;
static void
_class_lookUpIvar(Class cls, Ivar ivar, ptrdiff_t& ivarOffset,
objc_ivar_memory_management_t& memoryManagement)
{
ivarOffset = ivar_getOffset(ivar);
// 查找 ARC variables and ARC-style weak.
bool hasAutomaticIvars = NO;
for (Class c = cls; c; c = c->superclass) {
if (c->hasAutomaticIvars()) {
hasAutomaticIvars = YES;
break;
}
}
if (hasAutomaticIvars) {
Class ivarCls = _class_getClassForIvar(cls, ivar);
if (ivarCls->hasAutomaticIvars()) {
ptrdiff_t localOffset =
ivarOffset - ivarCls->alignedInstanceStart();
// 判断ivarLayout中成员变量offset对应的WORD是否保存id类型
if (isScanned(localOffset, class_getIvarLayout(ivarCls))) {
memoryManagement = objc_ivar_memoryStrong;
return;
}
// 判断weakIvarLayout中成员变量offset对应的WORD是否保存id类型
if (isScanned(localOffset, class_getWeakIvarLayout(ivarCls))) {
memoryManagement = objc_ivar_memoryWeak;
return;
}
// assign仅在ARC下才有效
if (ivarCls->isARC()) {
memoryManagement = objc_ivar_memoryUnretained;
return;
}
}
}
memoryManagement = objc_ivar_memoryUnknown;
}
static bool isScanned(ptrdiff_t ivar_offset, const uint8_t *layout)
{
if (!layout) return NO;
ptrdiff_t index = 0, ivar_index = ivar_offset / sizeof(void*);
uint8_t byte;
while ((byte = *layout++)) {
unsigned skips = (byte >> 4);
unsigned scans = (byte & 0x0F);
index += skips;
if (index > ivar_index) return NO;
index += scans;
if (index > ivar_index) return YES;
}
return NO;
}
4.2 类添加成员变量
用于添加成员变量class_addIvar(...)函数源代码有助于理解ivar_t几个成员变量的含义。
// class allocated but not yet registered
#define RW_CONSTRUCTING (1<<26)
// class allocated and registered
#define RW_CONSTRUCTED (1<<25)
// 添加成员变量
BOOL class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *type)
{
if (!cls) return NO;
if (!type) type = "";
if (name && 0 == strcmp(name, "")) name = nil;
rwlock_writer_t lock(runtimeLock);
assert(cls->isRealized());
// 元类不存在成员变量
if (cls->isMetaClass()) {
return NO;
}
// 仅在正在构建阶段的类才可以添加成员变量
if (!(cls->data()->flags & RW_CONSTRUCTING)) {
return NO;
}
// 匿名或不存在的成员变量才可添加。注意:不支持沿继承链搜索
if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
return NO;
}
class_ro_t *ro_w = make_ro_writeable(cls->data());
ivar_list_t *oldlist, *newlist;
if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) {
// 成员变量列表不为空,分配新成员变量列表内存,大小为旧成员变量列表占用空间加上单个元素占用空间
size_t oldsize = oldlist->byteSize();
newlist = (ivar_list_t *)calloc(oldsize + oldlist->entsize(), 1);
memcpy(newlist, oldlist, oldsize);
free(oldlist);
} else {
// 成员变量列表为空,直接分配ivar_list_t结构体占用空间的内存(只有首元素)
newlist = (ivar_list_t *)calloc(sizeof(ivar_list_t), 1);
newlist->entsizeAndFlags = (uint32_t)sizeof(ivar_t);
}
// 计算新增的成员变量在成员变量列表容器内存空间的偏移量
uint32_t offset = cls->unalignedInstanceSize();
uint32_t alignMask = (1<<alignment)-1;
offset = (offset + alignMask) & ~alignMask; // 设置对齐
// 获取保存新增成员变量的内存空间起始地址
ivar_t& ivar = newlist->get(newlist->count++);
ivar.offset = (int32_t *)malloc(sizeof(int32_t));
// 更新新增成员变量的偏移量
*ivar.offset = offset;
ivar.name = name ? strdup(name) : nil;
ivar.type = strdup(type);
ivar.alignment_raw = alignment;
ivar.size = (uint32_t)size;
// 成员变量列表指向新成员变量列表
ro_w->ivars = newlist;
cls->setInstanceSize((uint32_t)(offset + size));
return YES;
}
4.3 获取对象成员变量的值
构建类的实例时,按照成员变量列表分配内存空间,在访问实例的成员变量的值时,实例的类的成员变量列表起到至关重要的作用。调用object_getIvar(id obj, Ivar ivar)函数获取对象的成员变量。获取对象成员变量的值的大致流程是:
- 根据对象
isa指针获取对象的类; - 从类的成员变量列表中查询该成员变量的偏移量
offset; - 成员变量的值的地址 = 对象的起始地址 + 偏移量;
代码中,首先调用_class_lookUpIvar(...)查询成员变量信息,返回成员变量的偏移量offset和内存管理方式memoryManagement用于判断成员变量是否为weak类型;判断成员变量为 weak 时,调用id objc_loadWeak(id *location)获取成员变量的值,之所以这样处理是因为需要将返回的弱引用所指向的对象添加到 Autorelease Pool 中以保证对象能在弱引用的作用域内维持而不被立即释放。
id object_getIvar(id obj, Ivar ivar)
{
if (!obj || !ivar || obj->isTaggedPointer()) return nil;
ptrdiff_t offset;
objc_ivar_memory_management_t memoryManagement;
_class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
id *location = (id *)((char *)obj + offset);
if (memoryManagement == objc_ivar_memoryWeak) {
return objc_loadWeak(location);
} else {
return *location;
}
}
五、总结
-
成员变量记录了成员变量的变量名,还记录了成员变量在对象内存空间中的偏移量、成员变量数据类型、占用字节数以及对齐字节数,用
ivarLayout、weakIvarLayout记录成员变量的内存管理方式; -
新版本 runtime 支持 non-fragile instance variables,成员变量的偏移量并不是编译时固定,而是在运行时根据父类的
instanceSize动态调整; -
ivarLayout、weakIvarLayout数据形式时十六进制数,是对layout_bitmap中bits保存的二进制数压缩处理后的结果,layout_bitmap保存的二进制数记录了类对象内存空间中的instanceStart起始的类的成员变量内存空间中哪个 WORD 保存了id类型成员变量; -
ivarLayout、weakIvarLayout记录了某 WORD 保存id,则二进制该位置为1,称该对象中该成员变量scanned,ivarLayout中标记了scanned的成员变量内存管理方式为strong,weakIvarLayout中标记了scanned的成员变量内存管理方式为weak。 -
下一篇介绍方法列表的实现。