如何为分类(Category)添加成员变量
2019-10-26 本文已影响0人
打碟的DJ
关联对象涉及的三个方法
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
关联对象的本质
- 关联对象是由AssociationsManager管理并存储在AssociationsManager中
- 所有对象的关联内容都是存储在同一个全局容器中
源码分析
/**
* 关联对象
* @param object
* @param key
* @param value 值
* @param policy 策略
* @by Si
*/
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
assert(object);
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
// 获取新的值
id new_value = value ? acquireValue(value, policy) : nil;
{
// 关联对象管理类
AssociationsManager manager;
AssociationsManagerAssociationsManager &associations(manager.associations());
// 按位取反,作为关联对象的key
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
// 获取其维护的一个HashMap,是一个全局容器
AssociationsHashMap::iterator i = associations.find(disguised_object);
// 当前对象存在
if (i != associations.end()) {
// 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 {
// 不存在则生成一个ObjcAssociation
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// 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);
}