运行时之三:关联对象

通过运行时的关联对象我们可以为分类添加“成员变量”,但是这里所实现的效果只是看上去是给类添加了成员变量,并非真实的,因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的。那这个“成员变量”到底被添加到哪里去了呢?

关于这个问题,我们可以通过查看源码得到答案

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
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
//根据传入的value和策略 获取一个new_value,例如对value进行retain或copy
id new_value = value ? acquireValue(value, policy) : nil;
{
// 关联对象管理类,C++实现的一个类
AssociationsManager manager;
// 获取其维护的一个Hashmap,我们可以理解为是一个字典
// 是一个全局容器
AssociationsHashMap &associations(manager.associations());
// 对object指针按位取反作为HashMap中的key
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
//根据对象地址获取对应的ObjectAssociationMap对象
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {//ObjectAssociationMap对象存在
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);//根据传入的key获取其对应的关联对象
if (j != refs->end()) {//关联对象存在
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);//持有旧的关联对象
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);//直接存入新的关联对象
}
} else {
// create the new association (first time).
//为该对象创建一个ObjectAssociationMap对象,并存入新的关联对象,设置标志位
ObjectAssociationMap *refs = new ObjectAssociationMap;
//将ObjectAssociationMap 存入AssociationsHashMap中
associations[disguised_object] = refs;
//将新的关联对象存入ObjectAssociationMap中
(*refs)[key] = ObjcAssociation(policy, new_value);
//设置标志位 :设置object有了对象
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
//根据对象地址获取对应的ObjectAssociationMap对象
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {//ObjectAssociationMap对象存在
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);//根据传入的key获取其对应的关联对象
if (j != refs->end()) {//关联对象存在
old_association = j->second;//持有旧的关联对象
refs->erase(j);//擦除旧的关联记录
}
}
}
}
// release the old value (outside of the lock).
//旧的关联对象是否存在,存在的话 释放旧的关联对象
if (old_association.hasValue()) ReleaseValue()(old_association);
}

结论:

AssociationsManager 是顶级的对象,维护了一个 AssociationsHashMap 哈希表的单例键值对映射;

AssociationsHashMap 是一个无序的哈希表,维护了从对象地址到 ObjectAssociationMap 的映射;

ObjectAssociationMap 是一个 C++ 中的 map ,维护了从 keyObjcAssociation 的映射,即关联记录;

ObjcAssociation 是一个 C++ 的类,表示一个具体的关联结构,主要包括两个实例变量,_policy 表示关联策略,_value 表示关联对象。

每一个对象地址对应一个 ObjectAssociationMap 对象,而一个 ObjectAssociationMap 对象保存着这个对象的若干个关联记录。

关系图如下:

关联对象的本质

  • 关联对象由AssociationsManager管理并在AssociationsHashMap存储。
  • 所有对象的关联内容都在同一个全局容器中。