之前在Category一文中提到,能不能给Category添加成员变量。
回答是不可以的。
虽然我们可以在Category中声明属性,但是并不会生成成员变量,也不会有setter和getter方法。
如果我们想呢?可以利用关联对象实现这样一个效果。当然,肯定是通过runtime。
先代码实现一个效果。
//【Person类中,并没有name属性,只有一个sex属性】
Person *p = [[Person alloc] init];
//【通过关联对象,为p添加key=PERSON_NAME,value=aaa】
objc_setAssociatedObject(p, @"PERSON_NAME", @"aaa", OBJC_ASSOCIATION_COPY);
//【获取关联对象,取出p中PERSON_NAME的值】
id name = objc_getAssociatedObject(p, @"PERSON_NAME");
通过控制台,看到p中并没有PERSON_NAME。

设置关联对象
//【设置关联属性】
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(关联对象)
ObjcAssociation old_association(0, nil);
//【根据关联策略得到的值】
id new_value = value ? acquireValue(value, policy) : nil;
{
// 初始化一个manager
AssociationsManager manager;
//【通过manager,找到全局AssociationsHashMap】
AssociationsHashMap &associations(manager.associations());
//【获取对象的DISGUISE值,作为AssociationsHashMap的key】
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) { //【值存在,表示 添加 关联属性】
//【通过AssociationsHashMap的迭代器,查找object对应的ObjectAssociationMap】
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
//【找到object对应的关联对象表】
ObjectAssociationMap *refs = i->second;
//【通过迭代器,查找外部参数key所对应的关联属性】
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
//【找到外部参数key对应的关联属性】
//【将旧关联保存,用于最后的释放】
old_association = j->second;
//【设置为新的关联值】
j->second = ObjcAssociation(policy, new_value);
} else {
//【找不到参数key对应的关联属性】
//【通过构造函数增加】
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
//【object第一次添加关联对象】
//【为object创建关联表】
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
//【添加关联属性】
(*refs)[key] = ObjcAssociation(policy, new_value);
//【设置object的isa中的has_assoc值为true,表示有关联对象,用于后期对象的释放等操作】
object->setHasAssociatedObjects();
}
} else {
//【value无值,表示释放object中与key有关的关联属性】
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;
// 调用erase()方法删除对应的关联对象
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
// 释放旧的关联对象
if (old_association.hasValue()) ReleaseValue()(old_association);
}
各个结构在下文。
通过上面代码,我们梳理一下。
首先,关联对象的设置,是通过全局的一张关联表AssociationsHashMap管理的。
AssociationsHashMap可以理解为是一张表,其中key是对象的disguised_ptr_t值,value是与该对象有关的关联属性表ObjectAssociationMap。在ObjectAssociationMap这张表中,存放的是key和value,key为我们定义的名称,value则是key对应的ObjcAssociation。ObjcAssociation中存放的就是真正的值。
按照 AssociationsHashMap -> ObjectAssociationMap -> ObjcAssociation这个查找方式,设置。
如果在设置时,value有值,则意味着添加,无值则意味着移除关联。
上述是设置过程,我们再看下获取阶段。
获取关联对象
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) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
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()) {
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;
}
同样的,获取的过程 跟 设置的过程类似,主要是要找到key所对应的ObjcAssociation,然后再根据关联策略,管理引用计数。
移除对象的所有关联对象
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
// 移除对象object的所有关联对象
void _object_remove_assocations(id object) {
// 声明了一个vector
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 如果map size为空,直接返回
if (associations.size() == 0) return;
// 获取对象的DISGUISE值
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());
}
原理相似,不描述了。
相关结构
AssociationsManager
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
// AssociationsManager中只有一个变量AssociationsHashMap
static AssociationsHashMap *_map;
public:
// 构造函数中加锁
AssociationsManager() { AssociationsManagerLock.lock(); }
// 析构函数中释放锁
~AssociationsManager() { AssociationsManagerLock.unlock(); }
// 构造函数、析构函数中加锁、释放锁的操作,保证了AssociationsManager是线程安全的
AssociationsHashMap &associations() {
// AssociationsHashMap 的实现可以理解成单例对象
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsHashMap
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=对象的disguised_ptr_t值,value=ObjectAssociationMap。
ObjectAssociationMap
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); }
};
ObjcAssociation
class ObjcAssociation {
uintptr_t _policy; //【存放策略】
id _value; //【存放值】
public:
// 构造函数
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
// 默认构造函数,参数分别为0和nil
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
问题
-
如何移除某个key与对象的关联
设置key对应的value为nil即可。
-
为什么policy没有weak
weak修饰的属性,当没有拥有对象之后就会被销毁,并且指针置位nil,那么在对象销毁之后,虽然在map中既然存在值object对应的AssociationsHashMap,但是因为object地址已经被置位nil,会造成坏地址访问而无法根据object对象的地址转化为disguised_object(网上看到的)
-
关联对象是存储在对象的内存中吗?
不是,是统一放在一张全局的关联表中。
-
关联对象需要在对象的dealloc中释放吗?
不需要。在dealloc流程中,会解除关联。