之前我们分析过了Category本质,我们都知道Category不能直接添加成员变量,因为其内部结构没有存放成员变量的结构。
为了达到和正常类使用属性一样的效果,我们一般用runtime关联对象的方法来实现。
关联对象有这几个API
///获取某个对象的关联属性
id objc_getAssociatedObject(id object, const void *key)
///给某个对象添加关联属性
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
///移除对象所有的关联属性
void objc_removeAssociatedObjects(id object)
怎么使用不过多介绍了,直接分析内部实现原理。
在这些方法的实现里,有几个关键对象
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
先看下AssociationsManager
再看AssociationsHashMap
image.png
AssociationsHashMap是一个hashMap,和字典很像。继承unordered_map,这里是一个泛型。
再看ObjectAssociationMap
image.png
然后是ObjcAssociation
image.png
这几个对象的关系图,通过关系图应该比较好理解源码
image.png
- 然后我们直接分析源码,先看
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);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
//通过object经过运算得到disguised_object
disguised_ptr_t disguised_object = DISGUISE(object);
//new_value代表是否有传值
if (new_value) {
// break any existing association.
//通过disguised_object找到i,i->second得到ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
//通过key找到j,j->second得到ObjcAssociation(old_association),设置值
//如果没有找到j,设置新关联对象
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
//如果上面i没有找到ObjectAssociationMap,说明这个对象没有设置过关联对象,就new一个ObjectAssociationMap对象,再进行关联对象
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
//如果参数value为nil,就走这里关系对象在这里被移除,refs->erase(j)
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(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);
}
- 再看
objc_getAssociatedObject(id object, const void *key)
里面调用了_object_get_associative_reference(object, (void *)key)
还是直接上源码
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
//通过object运算得到disguised_object
disguised_ptr_t disguised_object = DISGUISE(object);
//disguised_object得到i,i->second得到ObjectAssociationMap *refs
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
//refs里通过key找j,j->second得到ObjcAssociation &entry,
//entry内部的value就是得到的value,policy就是关联对象策略
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
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(id object)
里面调用了_object_remove_assocations
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
//得到disguised_object
disguised_ptr_t disguised_object = DISGUISE(object);
//通过disguised_object获得i,i->second得到ObjectAssociationMap *refs
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;
//遍历ref获得j,移除j->second(关联对象)。等于是把关联对象的hashMap先清空,然后将对象的这个hasMap从全局的AssociationsHashMap中移除。
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
//移除了refs
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
最后总结:
关联对象并不是存储在被关联对象本身的内存中
关联对象是存储在全局统一的一个AssociationsManager中
设置关联对象value传nil,等于移除关联对象。
网友评论