Objective-C 引用计数
Objective-C 引用计数
存储引用计数
TaggedPointer
直接将其指针值作为引用计数返回
64 位 && Objective-C 2.0
isa 指针的一部分会用来存储引用计数
如果 isa 的引用计数溢出,使用一张 hash 表来存储
TaggedPointer
判断当前对象是否在使用 TaggedPointer 是看标志位是否为 1 :
1 |
|
isa 指针
优化的 isa 指针并不是就一定会存储引用计数,毕竟用 19bit (iOS 系统)保存引用计数不一定够。
需要注意的是这 19 位保存的是 引用计数的值减一。
has_sidetable_rc 的值如果为 1,那么引用计数会存储在一个叫 SideTable 的类的属性中
看源码
1 | // Define SUPPORT_NONPOINTER_ISA=1 to enable extra data in the isa field. |
hash 表存储
从上往下分析结构
SideTables
SideTables 是一个 64(iPhone 真机的情况下是 8)个元素长度的 hash 表,里面存储了 SideTable
键是对象的地址,值是对象的 SideTable
所以,一个 SideTable 可能对应多个对象
SideTables 在系统中是全局唯一的。
SideTables 的类型是是 template
class StripedMap,StripedMap 。 可以简单的理解为一个 64 * sizeof(SideTable) 的哈希线性数组
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
92
93
94
95
96
97
98
99
100
101
102
// SideTabls 可以通过全局的静态函数获取,实质是 StripedMap
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
// ...
enum { CacheLineSize = 64 };
// StripedMap 定义
// void* -> T
// StripedMap<T> is a map of void* -> T, sized appropriately
// for cache-friendly lock striping.
// For example, this may be used as StripedMap<spinlock_t>
// or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 }; // 真机 8
#else
enum { StripeCount = 64 };
#endif
// 封装 T
struct PaddedT {
T value alignas(CacheLineSize);
};
// PaddedT 类型的数组,长度是 StripeCount
PaddedT array[StripeCount];
// hash 定位算法
// 以 void* 作为 key,返回 void* 对应的 SideTable 的位置
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount; // 对 StripeCount 取余,防止越界
}
public:
// 对外的存取数据的方法
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(this)[p];
}
// 锁的相关操作,核心都是直接操作 array[i].value
// Shortcuts for StripedMaps of locks.
void lockAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.lock();
}
}
void unlockAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.unlock();
}
}
void forceResetAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.forceReset();
}
}
void defineLockOrder() {
for (unsigned int i = 1; i < StripeCount; i++) {
lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value);
}
}
void precedeLock(const void *newlock) {
// assumes defineLockOrder is also called
lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock);
}
void succeedLock(const void *oldlock) {
// assumes defineLockOrder is also called
lockdebug_lock_precedes_lock(oldlock, &array[0].value);
}
const void *getLock(int i) {
if (i < StripeCount) return &array[i].value;
else return nil;
}
#if DEBUG
StripedMap() {
// Verify alignment expectations.
uintptr_t base = (uintptr_t)&array[0].value;
uintptr_t delta = (uintptr_t)&array[1].value - base;
ASSERT(delta % CacheLineSize == 0);
ASSERT(base % CacheLineSize == 0);
}
#else
constexpr StripedMap() {}
#endif
};SideTable
从上面的 SideTables 的结构,可以得出一个结论:
一个 SideTable 可以对应多个对象
RefcountMap 是一个 hash 表,key 是对象的地址,值是引用计数减一
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
struct SideTable {
spinlock_t slock; // 保证原子操作的自旋锁
RefcountMap refcnts; // 保存引用计数的 hash 表,也就是 DenseMap
weak_table_t weak_table; // 保存弱引用的 hash 表
// 构造函数
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
// 析构函数 (实际上不能被析构)
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
// 一些锁的操作
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
}
RefcountMap
1
2
3
4
5
6
7struct RefcountMapValuePurgeable {
static inline bool isPurgeable(size_t x) {
return x == 0;
}
};
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;DenseMap
key:DisguisedPtr
,是对 objc_object * 指针及其一些操作进行的封装 value:__darwin_size_t,等价于 unsigned long,内容也是 引用计数减一
weak_table_t
关于 weak_table_t 的详细分析,见 Objective-C 弱引用,这里只展示源码
weak_table_t
1
2
3
4
5
6
7
8
9
10/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};weak_entry_t
关于 weak_entry_t 的详细分析,见 Objective-C 弱引用,这里展示下源码
weak_entry_t
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
/**
* The internal structure stored in the weak references table.
* It maintains and stores
* a hash set of weak references pointing to an object.
* If out_of_line_ness != REFERRERS_OUT_OF_LINE then the set
* is instead a small inline array.
*/
// out_of_line_ness field overlaps with the low two bits of inline_referrers[1].
// inline_referrers[1] is a DisguisedPtr of a pointer-aligned address.
// The low two bits of a pointer-aligned DisguisedPtr will always be 0b00
// (disguised nil or 0x80..00) or 0b11 (any other address).
// Therefore out_of_line_ness == 0b10 is used to mark the out-of-line state.
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
获取引用计数
MRC
1 | - (NSUInteger)retainCount { |
ARC
Core Foundation 库的 CFGetRetainCount()
Runtime 的 _objc_rootRetainCount(id obj)
修改引用计数
retain
引用计数 +1
_objc_rootRetain(id obj)
release
引用计数 -1
_objc_rootRelease(id obj)
alloc, new, copy, mutableCopy
引用计数 +1
autorelease
引用&参考
Objective-C 引用计数原理 by:杨潇玉
Objective-C runtime 机制(5)
Objective-C runtime 机制(7)
Objective-C 引用计数