底层初窥——关联对象

关联对象使用

为分类添加属性。(只是为属性添加了getter/setter方法,并没有生成带下划线的实例变量)。

1
2
3
4
5
6
7
8
9
10
11
12
13
void objc_setAssociatedObject(id object,
const void *key,
id value,
objc_AssociationPolicy policy)

// 1. 通过 key : value 的形式给对象 object 设置关联属性
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

// 2. 通过 key 获取关联的属性 object
id objc_getAssociatedObject(id object, const void *key);

// 3. 移除对象所关联的属性
void objc_removeAssociatedObjects(id object);
  • id object:被关联的对象
  • const void *key:关联的key,要求唯一
  • id value:给被关联的对象设置的值
  • objc_AssociationPolicy policy:内存管理的策略

关联对象源码解读

预备知识

在正式开始看这个源码之前,我们先来看看实现关联对象中涉及到的几个结构:

AssociationsManager

1
2
3
4
5
6
7
8
9
10
11
12
13
class AssociationsManager {
static spinlock_t _lock;
static AssociationsHashMap *_map; // 这个_ map 里边存储关联列表
public:
AssociationsManager() { _lock.lock(); }
~AssociationsManager() { _lock.unlock(); }

AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};

AssociationsManager 主要就是维护了一个 AssociationsHashMap,当第一次获取时,如果没有则会新建。并且在 AssociationsManager 所在作用域内,还有一个自旋锁 spinlock_t,防止在多线程情况下对 AssociationsHashMap 出现问题。

AssociationsHashMap

1
2
3
4
5
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
  • key: object经过特殊处理成为key
  • value: ObjectAssociationMap

ObjectAssociationMap

1
2
3
4
5
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
  • key: 设置关联对象时,传入的关联的 key(经过转换)
  • value:ObjcAssociation

ObjcAssociation

1
2
3
4
5
6
7
8
9
10
11
12
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}

uintptr_t policy() const { return _policy; }
id value() const { return _value; }

bool hasValue() { return _value != nil; }
};
  • 设置关联对象时设置的内存管理策略
  • 设置关联对象时传入的value

下边是这个 AssociationsManager 包含的层级关系:

objc_setAssociatedObject

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
///给某个对象添加关联属性
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}

// 通过对策略的判断返回不同的值
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// 初始化一个 old_association 对象,用于在 AssociationsManager 锁的内部,暂时持有原有值
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
// 取 manager,并取出其中的 AssociationsHashMap
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());

// 将传入的 object 做一次转换,作为 AssociationsHashMap 的 key 值
disguised_ptr_t disguised_object = DISGUISE(object);

// 传入设置的 value 有值的情况
if (new_value) {
// 根据 AssociationsHashMap 的 disguised_object 值,生成正向迭代器(ObjectAssociationMap)
AssociationsHashMap::iterator i = associations.find(disguised_object);

// 迭代器中有值
if (i != associations.end()) {
// i->second,找到 AssociationsHashMap 中 disguised_object 对应的 ObjectAssociationMap
ObjectAssociationMap *refs = i->second;

// 根据函数传入的 key 值,生成正向迭代器(ObjcAssociation)
ObjectAssociationMap::iterator j = refs->find(key);

// 迭代器中有值
if (j != refs->end()) {
// 找到 ObjectAssociationMap 中 key 对应的 ObjcAssociation
// 先用 old_association 保存原来的 ObjcAssociation
// 再根据传入的 new_value 及 policy 生成新的 ObjcAssociation 对象
// key: 新生成的 ObjcAssociation 对象
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
// 根据传入的 new_value 及 policy 生成新的 ObjcAssociation 对象
// key: 新生成的 ObjcAssociation 对象
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// 以 obejct 为 key,未找到 ObjectAssociationMap
// 则新建一个 ObjectAssociationMap
// disguised_object: ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;

// 根据传入的 new_value 及 policy 生成新的 ObjcAssociation 对象
// key: 新生成的 ObjcAssociation 对象
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
}
// 传入的 new_value 为 nil 的情况
else {
// 根据 AssociationsHashMap 的 disguised_object 值,生成正向迭代器(ObjectAssociationMap)
AssociationsHashMap::iterator i = associations.find(disguised_object);

// 迭代器中有值
if (i != associations.end()) {
// i->second,找到 AssociationsHashMap 中 disguised_object 对应的 ObjectAssociationMap
ObjectAssociationMap *refs = i->second;

// 根据函数传入的 key 值,生成正向迭代器(ObjcAssociation)
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 找到 ObjectAssociationMap 中 key 对应的 ObjcAssociation
// 先用 old_association 保存原来的 ObjcAssociation
// 将迭代器中的数据清除,即清除原 ObjcAssociation 对象
old_association = j->second;
refs->erase(j);
}
}
}
}
// 释放 old_association 对象
if (old_association.hasValue()) Rele aseValue()(old_association);
}

struct ReleaseValue {
void operator() (ObjcAssociation &association) {
releaseValue(association.value(), association.policy());
}
};
  • 结论

  • 一个实例对象对应一个 ObjectAssociationMap,而 ObjectAssociationMap 里面存储着多个此实例对象的关联对象的 key 以及 ObjcAssociation,ObjcAssociation 中存储着关联对象的 value 和 policy。

  • 设置关联对象时,传入的 key 值必须是唯一的。

  • 关联属性的内存管理是存储在一个全局的 AssociationsManager 中,通过 HashMap 进行管理。

objc_getAssociatedObject

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
///获取某个对象的关联属性
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}

id _object_get_associative_reference(id object, void *key) {
// 初始化 value 及 policy,承载取出 ObjcAssociation 实例对象的值,
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
// 获取 AssociationsHashMap
AssociationsHashMap &associations(manager.associations());
// 转换 object 作为 AssociationsHashMap 的 key 值
disguised_ptr_t disguised_object = DISGUISE(object);
// 根据 AssociationsHashMap 的 disguised_object 值,生成正向迭代器(ObjectAssociationMap)
AssociationsHashMap::iterator i = associations.find(disguised_object);
// 迭代器中有值
if (i != associations.end()) {
// i->second,找到 AssociationsHashMap 中 disguised_object 对应的 ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
// 根据函数传入的 key 值,生成正向迭代器(ObjcAssociation)
ObjectAssociationMap::iterator j = refs->find(key);
// 迭代器中有值
if (j != refs->end()) {
// 找到 ObjectAssociationMap 中 key 对应的 ObjcAssociation
// 将 ObjcAssociation 实例对象的 value 及 policy 赋值给开始定义的局部变量
ObjcAssociation &entry = j->second;
value = entry.value();
policy = entry.policy();
// 根据内存管理策略来对关联对象进行内存管理
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
// 根据内存管理策略来对关联对象进行内存管理
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}

objc_removeAssociatedObjects

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
///移除对象所有的关联属性
void objc_removeAssociatedObjects(id object)

void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}

关联对象如何实现weak属性

设置关联对象的方法中,传入的 policy 枚举值如下:

1
2
3
4
5
6
7
8
9
10
11
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};

要实现 weak ,说白了就是要做到两点:

  1. 引用计数器不变;
  2. 对象销毁后自动设置为 nil。

而在 runtime 所提供的枚举中,OBJC_ASSOCIATION_ASSIGN 就已经做到了第一点,我们只需要实现第二点即可。第二点是要在对象销毁后,将 weak 引用设置为 nil ,所以我们要捕获这个对象销毁的时机,或者接收这个对象销毁的事件。在 ARC 中,对象销毁时机其实就是 dealloc 方法调用的时机,我们可以在这个方法里将这个 weak 引用设置为 nil。

SomeClass+WeakProperty –> ObjectValue –> DeallocBlock –> SomeClass+WeakProperty

当设置 SomeClass+WeakProperty 值时,我们再动态地为 ObjectValue 添加一个 DeallocBlock,在这个 Block 中,我们将 SomeClass+WeakProperty 的属性置为 nil。然后,在 ObjectValue 的 dealloc 方法中,执行自己持有的 DeallocBlock。

// 待后续更新代码