objc_setAssociatedObject
属性关联可以在运行时动态的给某个对象添加属性,来看objc_setAssociatedObject的实现:
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
_object_set_associative_reference函数首先初始化了一个ObjcAssociation对象
ObjcAssociation old_association(0, nil)
old_association用来存储关联的值和关联策略,默认是OBJC_ASSOCIATION_ASSIGN。接下来如果发现传入的value不为空,那么就对传入的value进retain或者copy操作(如果关联策略是OBJC_ASSOCIATION_SETTER_RETAIN或者OBJC_ASSOCIATION_SETTER_COPY)
id new_value = value ? acquireValue(value, policy) : nil;
//acquireValue函数的实现
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
接下来初始化AssociationsManager,AssociationsManager里面主要是存在一个hash map,结构如下:
class AssociationsManager {
static spinlock_t _lock;
static AssociationsHashMap *_map; // associative references: object pointer -> PtrPtrHashMap.
public:
AssociationsManager() { _lock.lock(); }
~AssociationsManager() { _lock.unlock(); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
并且在初始化的时候获取一把自旋锁,对象析构的时候释放锁。AssociationsManager类结构里边的锁对象和map对象都是静态的,以便下次直接获取,也就是说是全局唯一的。接下来获取map对象
AssociationsHashMap &associations(manager.associations());
//把对象转成unsigned long类型数据,获取一个索引
disguised_ptr_t disguised_object = DISGUISE(object);
接下里判断如果传入的value不为空,那么就通过disguised_object从AssociationsHashMap中取出储存被关联的对象的ObjectAssociationMap,ObjectAssociationMap中存储的是该对象被关联所有的value。接下来通过传入的key看对象有没有关联过这个value,如果有就直接赋值,如果没有就创建一个。
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
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);
}
如果AssociationsHashMap从没有对象的关联信息表,那么就创建一个map并通过传入的key把value存进去
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
如果传入的value是nil,并且之前使用相同的key存储过关联对象,那么就把这个关联的value移除(这也是为什么传入nil对象能够把对象的关联value移除)
最后把之前使用传入的这个key存储的关联的value释放(OBJC_ASSOCIATION_SETTER_RETAIN策略存储的)
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
objc_getAssociatedObject
objc_getAssociatedObject用来获取关联的value
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) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
}
return value;
}
实现也比较简单,还是通过对应的key在map中取出value返回,这里不再赘述。
objc_remove_assocations
移除对象的关联引用通过_object_remove_assocation函数实现,这里主要是取出对象的关联引用map,然后依次对其中的对象发送release消息(前提是关联的策略是copy或者retain)。具体实现如下:
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());
}
我们可以看到,AssociationsHashMap先是先是移除了对象的ObjectAssociationMap
// 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);
最后通过for_each(elements.begin(), elements.end(), ReleaseValue());
来释放每一个对象。其中ReleaseValue重载了运算符(),具体release的过程如下:
struct ReleaseValue {
void operator() (ObjcAssociation &association) {
releaseValue(association.value(), association.policy());
}
};
static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
}
}
在实际开发中其实我们并不需要手动调用objc_remove_assocations函数,apple在dealloc中自动帮我们调用了该函数。再回顾一下dealloc的过程,dealloc主要调用了void *objc_destructInstance(id obj)
函数,该函数中有这样一行代码:
if (isa->instancesHaveAssociatedObjects()) {
_object_remove_assocations(obj);
}
apple就是在这里释放了对象的关联引用。