__weak 实现分析

文章目录
  1. 1. 前言
  2. 2. weak 源码分析
    1. 2.1. SideTable

前言

runtime 维护了一个weak表,用于存储指向某个对象的所有weak指针。
weak表其实是一个hash(哈希)表:

  • key是所指对象的地址
  • value是weak指针的地址(这个地址的值是所指对象指针的地址)数组

weak 源码分析

初始化一个新的weak指针指向对象的地址

__weak变量的存储

1
2
NSObject *obj = [[NSObject alloc] init];
id __weak obj1 = obj;
1
2
3
4
/* 编译器模拟代码 */
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);
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
/** 
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr.
* @param newObj Object ptr.
*/
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
id objc_initWeak(id *location, id newObj){
if (!newObj) {
*location = nil;
return nil;
}

return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
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
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj) {
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;

// Acquire locks for old and new values.
// Order by lock address to prevent lock ordering problems.
// Retry if the old value changes underneath us.
retry:
if (haveOld) {
oldObj = *location;
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}

SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}

// Prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
class_initialize(cls, (id)newObj);

// If this class is finished with +initialize then we're good.
// If this class is still running +initialize on this thread
// (i.e. +initialize called storeWeak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// Instead set previouslyInitializedClass to recognize it on retry.
previouslyInitializedClass = cls;
goto retry;
}
}

// Clean up old value, if any.
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}

// Assign new value, if any.
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected

// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}

// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}

SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

return (id)newObj;
}

SideTable

1
2
3
4
5
6
7
8
9
struct SideTable {
// spinlock_t 内部实现是 os_unfair_lock,用于对SideTable操作时进行加锁
spinlock_t slock;
// 存储对象的引用计数
RefcountMap refcnts;
// 存储对象的弱引用
weak_table_t weak_table;
...
}

从 SideTables 是一个 StripedMap

1
2
3
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<typename T>
class StripedMap {
// 定义散列表的大小
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif

struct PaddedT {
T value alignas(CacheLineSize);
};

PaddedT array[StripeCount];

// 散列函数:通过指针地址计算出index
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
...
}

在发生散列冲突的时候,多个对象会共用一个SideTable,也就是多个对象会共用一个refcnts和weak_table。

weak_table_t

1
2
3
4
5
6
7
8
9
10
struct weak_table_t {
// 存储weak_entry_t
weak_entry_t *weak_entries;
// 当前weak_entry_t的个数
size_t num_entries;
// 当前数组能够容纳的最大个数
uintptr_t mask;
// 发生散列冲突时,能够遍历的元素的最大个数
uintptr_t max_hash_displacement;
};