Runtime之对象的一生

Runtime

在所有的面向对象语言中,万物皆对象。Objective-C也不例外,这篇文章我们从Runtime的源码去分析一个对象从创建到销毁的整个过程中都做了哪些操作,同时也加深我们对内存管理的理解。

对象的创建

我们平时开发中创建一个对象的方式一般有三种

  • alloc init
1
NSString *str = [[NSString alloc] init];
  • new
1
NSString *str = [NSString new];
  • 类方法构建
1
NSMutableString *str = [NSMutableString string];

那么在这个过程中系统都做了哪些操作呢?这三种不同的创建方式又会有什么区别呢?下面我们对上述的几个关键词一一介绍然后在分析下这几种创建对象方式的区别。

alloc

我们先来看下alloc方法在Runtime中的实现

1
2
3
+ (id)alloc {
return _objc_rootAlloc(self);
}

我们在进一步看下_objc_rootAlloc方法的实现:

1
2
3
4
5
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

我们继续跟进方法实现:

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
// ALWAYS_INLINE 强制内联函数 所有加了__attribute__((always_inline))的函数再
// 把被调用时不会被编译成函数调用而是直接扩展到调用函数体内
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
// slowpath 表示if条件为0的可能性较大 利于编译器优化指令跳转
// 如果需要检查nil 且cls 为nil 那么直接返回nil
// 因此 如果cls不存在使用alloc方法创建时可能返回nil
if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
// fastpath 表示if条件中为1的可能性较大
// 如果类没有自定义的allocwithzone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// cls 是否可以快速创建
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
// cls是否有c++销毁方法 dtor = destructor
bool dtor = cls->hasCxxDtor();
// calloc 在内存中动态地分配 1 个长度为 cls->bits.fastInstanceSize() 的连续空间
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
// 如果obj为空 类初始化失败 报错 attempt to allocate object of class '%s' failed
if (slowpath(!obj)) return callBadAllocHandler(cls);
// 调用初始化对象isa方法
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
// 调用class_createInstance创建对象
id obj = class_createInstance(cls, 0);
// 如果对象为空 那么直接报错
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif

// No shortcuts available.
// alloc 方法会进一步调用allocwithzone方法
if (allocWithZone) return [cls allocWithZone:nil];
// 正常情况不会走到这里 想到与一个递归?
return [cls alloc];
}

我们将这个方法的流程通过下图来更好的了解下:

注意:图中深色背景区域为OBJC2下会走的的位置,基本上目前都会做到这里

为了更好的了解这个方法的实现我们进一步看下这个方法中调用的几个方法:

写完这个方法后,发现在最新的Runtime(781)源码中这个方法变成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
// 是否有自定义的allocWithZone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 直接调用_objc_rootAllocWithZone
return _objc_rootAllocWithZone(cls, nil);
}
#endif

// No shortcuts available.
// 是否需要调用allocWithZone _objc_rootAlloc方法调用时这个参数为yes
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
// 调用alloc方法
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

实际上在OBJC2中这个方法的改变并不大,下面我们看下上面这两种实现用到的主要方法的实现,

objc_rootAllocWithZone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//allocWithZone的根方法
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
// 如果zone未空 那么直接调用class_createInstance方法 与之前750的实现一致
if (fastpath(!zone)) {
obj = class_createInstance(cls, 0);
} else {
// 如果返回的zone有值 那么调用class_createInstanceFromZone传入
obj = class_createInstanceFromZone(cls, 0, zone);
}
// 如果创建的obj为空
if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls);
return obj;
}

这个方法实际上只是做了一层简单的包装,我们重点关注class_createInstanceclass_createInstanceFromZone方法。

class_createInstance

1
2
3
4
5
6
id
class_createInstance(Class cls, size_t extraBytes)
{
if (!cls) return nil;
return _class_createInstanceFromZone(cls, extraBytes, nil);
}

我们继续看_class_createInstanceFromZone方法(即_objc_rootAllocWithZone方法中zone不为空时调用的方法):

1
2
3
4
5
6
7
8
NEVER_INLINE
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}

继续深入_objc_rootAllocWithZone方法:

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
// 创建对象
// cls 创建对象的类型
// extraBytes 额外字节 正常alloc方法这个值为0
// zone construct_flags outAllocatedSize
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();
// canAllocNonpointer:class's instances requires raw isa
// #define FAST_CACHE_REQUIRES_RAW_ISA (1<<13)
// 是否是 isa_t 类型的 isa
bool fast = cls->canAllocNonpointer();

size_t size;
// 增加额外字节
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;

id obj;
// 新创建一个obj 并指向一块新创建的内存空间
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
// 如果对象创建失败 obj = nil
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}

// isa如果是isa_t类型
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
// isa是cls类型
obj->initIsa(cls);
}
// 如果没有自定义构建方法
if (fastpath(!hasCxxCtor)) {
return obj;
}
// 自定义构建方法调用
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}

我们在看下这个方法中用到的几个方法:

hasCxxDtor

1
2
3
4
5
6
7
8
9
10
11
12
bool hasCxxDtor() {
// addSubclass() propagates this flag from the superclass.
assert(isRealized());
return bits.hasCxxDtor();
}

bool hasCxxDtor() {
return data()->flags & RW_HAS_CXX_DTOR;
}

// class or superclass has .cxx_destruct implementation
#define RW_HAS_CXX_DTOR (1<<17)

从上述代码中,我们可以看到hasCxxDtor表示cls或者supercls是否有销毁(cxx_destruct)方法。

canAllocNonpointer

1
2
3
4
5
6
7
8
9
10
11
BOOL canAllocNonpointer() {
ASSERT(!isFuture());
return !instancesRequireRawIsa();
}

BOOL instancesRequireRawIsa() {
return cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);
}

// class's instances requires raw isa
#define FAST_CACHE_REQUIRES_RAW_ISA (1<<13)

initInstanceIsa

1
2
3
4
5
6
7
8
inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
// 相当于也是包装了一层
initIsa(cls, true, hasCxxDtor);
}

initInstanceIsa主要是调用initIsa方法,我们在进一步看下这个方法的实现:

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
inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
// 如果是非isa_t类型 直接返回 这个cls即可 因为isa中只有一个指针指向isa
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
// 创建 isa_t 类型临时变量
isa_t newisa(0);

// 配置 magic 表示当前对象已经创建
// 配置 nonpointer 表示当前对象的 isa 为 isa_t 类型的
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
// 配置 has_cxx_dtor 表示当前对象是否有 C++ 的析构器
newisa.has_cxx_dtor = hasCxxDtor;
// 配置 shiftcls 指向类对象,右移了 3 位是因为类的指针是按照字节(8bits)对齐的,
// 其指针后三位都是没有意义的 0,因此可以右移 3 位进行消除,以减小无意义的内存占用。
newisa.shiftcls = (uintptr_t)cls >> 3;

// 将临时变量赋值给结构体成员
isa = newisa;
}
}

从上面我们看出如果是isa_t类型,我们主要设置ISAbitshas_cxx_dtorshiftcls三个属性。

hasCustomAWZ

1
2
3
4
5
6
7
8
9
10
11
    bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}

bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}

// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define RW_HAS_DEFAULT_AWZ (1<<16)

RW_HAS_DEFAULT_AWZ这个标志位表明是cls或者superclass是否有默认的alloc/allocWithZone方法实现,hasCustomAWZ的意思是是否有自定义的AWZ(allocWithZone)方法,在我们前面的NSObject结构这篇文章中我们介绍过下面这张图

在图中我们看到NSObject结构中有一个uint32_t类型的flags,而hasDefaultAWZ方法就是从我们的NSObject结构中通过按位与的方法获取到这个值。

object_cxxConstructFromClass

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
// 递归调用对象的C++构造方法,从基类到子类的构造方法
id
object_cxxConstructFromClass(id obj, Class cls, int flags)
{
ASSERT(cls->hasCxxCtor()); // required for performance, not correctness

id (*ctor)(id);
Class supercls;

supercls = cls->superclass;

// Call superclasses' ctors first, if any.
// 如果父类有C++构造方法
if (supercls && supercls->hasCxxCtor()) {
// 调用父类构造方法
bool ok = object_cxxConstructFromClass(obj, supercls, flags);
// 父类构造方法失败 返回nil
if (slowpath(!ok)) return nil; // some superclass's ctor failed - give up
}

// Find this class's ctor, if any.
// 父类构造方法调用完成后调用当前类的SEL_cxx_construct方法
ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
// 如果当前类的构造为转发方法 那么直接返回 表示没有对应实现
if (ctor == (id(*)(id))_objc_msgForward_impcache) return obj; // no ctor - ok

// Call this class's ctor.
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ constructors for class %s",
cls->nameForLogging());
}
// 调用构造方法 传入参数obj
if (fastpath((*ctor)(obj))) return obj; // ctor called and succeeded - ok

supercls = cls->superclass; // this reload avoids a spill on the stack

// This class's ctor was called and failed.
// Call superclasses's dtors to clean up.
if (supercls) object_cxxDestructFromClass(obj, supercls);
if (flags & OBJECT_CONSTRUCT_FREE_ONFAILURE) free(obj);
if (flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}

allocWithZone

1
2
3
4
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}

这里实际上又回到了上面的_objc_rootAllocWithZone方法,与OBJC2的区别只是是否做了cls为nil的判断以及是否有自定义的allocWithZone的方法。

1
2
3
4
5
6
if (slowpath(checkNil && !cls)) return nil;
// 是否有自定义的allocWithZone方法
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// 直接调用_objc_rootAllocWithZone
return _objc_rootAllocWithZone(cls, nil);
}

alloc方法调用完成之后,我们实际上已经开辟了对应的内存空间初始化好了ISA的部分信息,下面我们来看下init方法。

init

1
2
3
- (id)init {
return _objc_rootInit(self);
}
1
2
3
4
5
6
7
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}

从这里我们看到init方法实际上并没有实现什么只是将我们在alloc方法创建好的对象返回了。

new

1
2
3
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}

我们看到这个方法实际上就是调用了callAlloc方法然后callAlloc返回的对象在调用init方法。

至此对象创建的几种方式我们就分析完成了。

对象创建流程如下(主要是alloc方法):

对象赋值

看完了对象的初始化,我们在平时常用的就是给对象赋值和拷贝,比如

1
2
Person *person = [[Person alloc] init];
Person *personCpy = [person copy];

下面我们主要从上面这两个方面来看下在这个过程中Runtime都做了什么:

赋值

我们都知道,对于一个对象的引用我们有弱引用和强引用两种,分别用__weak__strong来修饰,如果我们不做显示说明那么默认是__strong

我们在Runtime中去查找修饰变量的类型时发现了下面这个枚举:

1
2
3
4
5
6
7
8
9
10
11
12
/*
"Unknown" includes non-object ivars and non-ARC non-__weak ivars
"Strong" includes ARC __strong ivars
"Weak" includes ARC and new MRC __weak ivars
"Unretained" includes ARC __unsafe_unretained and old GC+MRC __weak ivars
*/
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;

下面我们来详细的了解下这其中的区别:

__strong

上面看到的

1
Person *stu = [[Person alloc] init];

实际上可以翻译成:

1
__strong Person *stu = [[Person alloc] init];

那么__strong都做了什么呢?我们直接去看下objc_storeStrong方法都做了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
// 先判断之前的值和要新赋值的对象地址是否相同
if (obj == prev) {
return;
}
// 执行retain操作 保留新值
objc_retain(obj);
*location = obj;
// 释放旧值
objc_release(prev);
}

这个方法的主要操作让我们很容易联想到MRCsetter方法的书写方式,保留新值,释放旧值。我们在进一步看下objc_retainobjc_release的实现。

objc_retain

1
2
3
4
5
6
7
8
9
10
11
__attribute__((aligned(16), flatten, noinline))
id
objc_retain(id obj)
{
// 如果为nil 直接返回nil
if (!obj) return obj;
// 如果是taggedPointer 直接返回
if (obj->isTaggedPointer()) return obj;
// 调用retain方法
return obj->retain();
}

这个方法里 判断了如果要被retain的对象为nil或者是TaggedPointer类型那么直接返回否则调用retain方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 等价于直接使用对象调用retain方法
inline id
objc_object::retain()
{
// 如果是TaggedPointer类型 不涉及引用计数
ASSERT(!isTaggedPointer());
// fastpath 表示if中的条件是一个大概率事件
// 如果当前对象没有自定义(override)retain 方法
if (fastpath(!ISA()->hasCustomRR())) {
return rootRetain();
}
// 如果有自定义的retain方法
// 通过发消息的方式调用自定义的 retain 方法
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}

这个方法实际上最后调用了rootRetain,对于这个方法我们在
retain和release实现探究这篇文章中详细的介绍过。这里我们就不做进一步的介绍了。我们只需要知道在这个方法中我们newisa.nonpointer判断需要增加哪里的引用计数:

  • 如果newisa.nonpointer = 0 则没有进行runtime优化,在sidetable中找到这个对象对应的节点更新将SideTable该对象对应的节点的refcntStorage值 + 1
  • 如果newisa.nonpointer = 1 则进行了runtime优化,那么直接在newisa.extra_rc上做引用计数+1同时需要判断newisa.extra_rc是否溢出,如果溢出则需要将一半的引用计数放到sidetablerefcntStorage中同时将newisa.has_sidetable_rc置为true。

从上面可以看出objc_retain的操作实际是:将新对象的引用计数+1,将旧对象的引用计数-1

__weak

当我们用__weak去修饰一个变量时根据上面的介绍我们实际上是调用objc_storeWeak方法,我们直接看下这个方法的实现。

1
2
3
4
5
6
7
// location weak指针自身的地址
// newObj weak指针指向的新对象的地址
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);

这个方法内部实际调用了storeWeak方法,这个方法与rootRetain一起,我们在retain和release实现探究这里都有详细的讲解这里不再赘述。

我们大概描述下storeWeak操作做了哪些事情:

  • 根据对象地址从SideTable中取出对应的节点,如果之前没有 则创建一个新的
  • 调用weak_unregister_no_lock,将weak pointer的地址从旧对象的weak_entry_t中移除,同时判断是否weak_entry_t为空,则将weak_entry_tweaktable中移除。
  • 调用weak_register_no_lock方法,将weak pointer的地址(location)记录到对象对应的weak_entry_t中,并将对象的isa指针中的weakly_referenced标志位置为1,表明这个对象被弱引用了

retaincount

我们上面创建或者给一个strong类型的指针赋值,实际上都是对引用计数的+1操作,那么我们看下这个引用计数是如何保存的呢?

1
2
3
4
5
6
7
8
9
10
11
12
- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}

uintptr_t
_objc_rootRetainCount(id obj)
{
ASSERT(obj);

return obj->rootRetainCount();
}

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
// 获取引用高技术
inline uintptr_t
objc_object::rootRetainCount()
{
//isTaggedPointer 不需要引用计数
if (isTaggedPointer()) return (uintptr_t)this;

sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
// 如果是nonpointer
if (bits.nonpointer) {
// 先从extra_rc取出部分引用计数
uintptr_t rc = 1 + bits.extra_rc;
// sidetable中是否有额外的引用计数
if (bits.has_sidetable_rc) {
// 从sidetable中获取引用计数
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}

sidetable_unlock();
// 如果不是nonpointer 直接从sidetable中获取引用计数
return sidetable_retainCount();
}

如果对象是TaggedPointer 直接返回这个对象(TaggedPointer不需要引用计数),否则

  • bits.nonpointer = 1 则先从bits.extra_rc获取部分引用计数 然后判断sidetable中是否有额外的引用计数 如果有 二者相加
  • bits.nonpointer = 0 则直接从sidetable中获取引用计数

对象的销毁

对象常用的销毁方法实际上就是releaseautorelease两种。我们来看下这两种的具体实现:

release

在查看源码前,我们知道release的主要功能是:引用计数-1,我们来看下具体实现

1
2
3
- (oneway void)release {
_objc_rootRelease(self);
}

我们看下_objc_rootRelease的实现:

1
2
3
4
5
6
7
NEVER_INLINE void
_objc_rootRelease(id obj)
{
ASSERT(obj);

obj->rootRelease();
}

rootRelease方法我们在retain和release实现探究中也有详细介绍。

我们简单的说下rootRelease这个方法:

  • 如果newisa.nonpointer = 0 则调用sidetable_release方法,这个方法会判断sidetablerefcnt是否小于SIDE_TABLE_DEALLOCATING 如果小于则表示这个对象要被释放,则调用dealloc方法。如果不小于则直接执行refcnt -= SIDE_TABLE_RC_ONE;即refcnt-1操作。
  • 如果newisa.nonpointer = 1 则 对newisa.extra_rc进行-1操作,然后判断newisa.extra_rc是否有向下溢出,并执行对应操作。

autorelease

1
2
3
- (id)autorelease {
return _objc_rootAutorelease(self);
}
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
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}

static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}

static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}

我们看到autorelease实际上最终是调用了AutoreleasePoolPageautorelease方法,AutoreleasePoolPage我们在iOS内存管理之AutoreleasePool中有详细介绍,这里我们也不再赘述。

我们简单的回忆下AutoreleasePoolPage几个重要方法:

  • push

    1、首先取出当前的hotPage,所谓hotPage,就是在autoreleasePage链表中正在使用的autoreleasePage节点。
    2、如果有hotPage,且hotPage还没满,这将obj加入到page中。
    3、如果有hotPage,但是已经满了,则进入page full逻辑(autoreleaseFullPage)
    4、如果没有hotPage,进入no page逻辑autoreleaseNoPage

  • pop(void pop(void *token))

    autoreleasepool需要被释放时,会调用Pop方法。而Pop方法需要接受一个void *token参数,来告诉池子,需要一直释放到token对应的那个page

通过上面的介绍,我们知道AutoreleasePoolPage实际上就是一个存放待释放对象的缓存池。push为追加对象,pop为释放对象。

dealloc

在上面我们介绍release时,我们发现在对对象的引用计数进行-1操作时,如果判断对象引用计数为0,会主动调用dealloc,那么我们看下这个方法的实现

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
- (void)dealloc {
_objc_rootDealloc(self);
}

_objc_rootDealloc(id obj)
{
ASSERT(obj);

obj->rootDealloc();
}

inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?

if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}

我们看到这个方法主要逻辑为:

  • 如果对象isa.nonpointer=1且没有弱指针指向他,且没有关联对象,没有自定义销毁方法,sidetableretainCount = 0 则直接释放
  • 如果对象isa.nonpointer=0 则需要调用object_dispose方法

我们在看下object_dispose方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
static id 
_object_dispose(id anObject)
{
if (anObject==nil) return nil;
// 调用objc_destructInstance
objc_destructInstance(anObject);
// isa指针为_objc_getFreedObjectClass类型
anObject->initIsa(_objc_getFreedObjectClass ());
// 释放对象
free(anObject);
return nil;
}

我们进一步看下_object_dispose中的这几个方法

objc_destructInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 对象的析构方法
void *objc_destructInstance(id obj)
{
if (obj) {
// 获取到isa
Class isa = obj->getIsa();
// 是否有c++析构方法 有则执行
if (isa->hasCxxDtor()) {
object_cxxDestruct(obj);
}
// 是否有关联对象 有则移除
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
// 调用objc_clear_deallocating方法
objc_clear_deallocating(obj);
}

return obj;
}

initIsa

这个方法我们在上面介绍过了 这里主要看下参数_objc_getFreedObjectClass (),我们看下系统给的解释

1
2
3
4
5
6
7
8
9
10
/***********************************************************************
* _class_getFreedObjectClass. Return a pointer to the dummy freed
* object class. Freed objects get their isa pointers replaced with
* a pointer to the freedObjectClass, so that we can catch usages of
* the freed object.
**********************************************************************/
static Class _class_getFreedObjectClass(void)
{
return (Class)freedObjectClass;
}

我们可以简单的理解为,对于一个已释放的对象他的isa指针指向了freedObjectClass类型,系统以此作为已释放对象的标记。

我们在源码中搜索的时候还发现:

1
2
3
if (anObject->ISA() == _objc_getFreedObjectClass ())
__objc_error(anObject, "reallocating freed object");

重新分配已释放对象的空间,这也作证了我们上面描述的。

objc_clear_deallocating

1
2
3
4
5
6
7
8
9
10
11
12
13
14
inline void 
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}

assert(!sidetable_present());
}

这个方法也是根据isa.nonpointer判断:

  • 如果isa.nonpointer = 0 调用sidetable_clearDeallocating 执行sidetable相关的释放
  • 如果isa.nonpointer = 1 调用clearDeallocating_slow 进行释放

sidetable_clearDeallocating

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void 
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];

// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
  • 根据this取出对象对应的sidetable 如果有弱引用指针指向这个对象 则这里进行置nil 调用的是weak_clear_no_lock方法
  • 调用erase方法将table.refcnts置为空

clearDeallocating_slow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));

SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}

实际上这个方法和sidetable_clearDeallocating方法的实现高度相似,只是标志位一个是从it->second通过按位与方法判断一个是直接从isa中获取。

总结

这篇文章我们从对象的创建以及赋值销毁等方面分析了Runtime的实现,尤其是对于引用计数器的操作。让我们对内存管理有了更清晰的认识。

参考文章

OC对象从创建到销毁
Objc 对象的今生今世