从【nonpointer】探究OC类中isa指针

前言

在这篇文章从【class_getInstanceSize方法】探究iOS的内存分配策略中,我们分析了OC类的内存分配策略,但是具体的初始化过程并没有提及,这里我们来探究下OC在内存的初始化过程中都做了什么。

源码分析

我们还是先看一下整个OC类的创建流程,如下图:

其中标红的_class_createInstanceFromZone函数就是核心内容,我们直接来看看其内部实现逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer(); //是否支持nonpointer 这个是关键
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor); // 支持nonpointer的类初始化过程 见下面的分析
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls); // 不支持nonpointer的类初始化过程 见下面的分析
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags); // 默认添加一个cxx的析构函数
}
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor); // nonpointer直接写死为true
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer()); // 小对象类型直接返回 没有isa指针
if (!nonpointer) {
isa = isa_t((uintptr_t)cls); // 不支持nonpointer的对象直接将类对象赋值给isa
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA //判断平台
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3; // 前三位都不是存的类信息 所以先抛弃前三位再赋值
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}

从上面的源码中我们看到初始化的过程实际上就是对isa指针赋值的过程,对于不满足nonpointer的对象,isa指针直接等于类对象,而nonpointer对象的isa就比较复杂了,这里我们再来分析下nonpointer下的isa指针的数据结构,如下:

isa结构分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// isa_t 也就是isa指针的结构类型是一个联合体,cls和bits共享64位的内存空间
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
};
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; //是否是nonponiter类型 \
uintptr_t has_assoc : 1; //是否有关联对象 \
uintptr_t has_cxx_dtor : 1; //是否添加了cxx的析构函数 \
uintptr_t shiftcls : 33;//类对象信息 /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; //魔数 \
uintptr_t weakly_referenced : 1; //是否有弱引用 \
uintptr_t deallocating : 1; //是否正在析构 \
uintptr_t has_sidetable_rc : 1; //引用计数表中是否存了该对象的引用计数 \
uintptr_t extra_rc : 19 //引用计数
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)

可视化结构

结合上面的源码和图,nonpointer的isa指针的结构应该就比较清楚了。这个结构我们之后在分析iOS的内存管理的时候应该还会在具体分析。这里暂时先只做了解。

小结

通过以上分析,OC类的创建过程就比较清晰了,首先是通过内存对齐原则计算需要分配的内存大小,然后对类的isa指针进行初始化。到这里就可以确定一个类的对象了,那么类中superclass,cache(第一次有消息发送行为时创建内存),bits等是在何时初始化的呢,这个留在以后探究。