咱们都知道,在NSObject对象中,咱们能够建立一个category,来对object作一些辅助性质的工做,好比代码的结偶啊等等,能够动态的为对象添加一些新的行为。那么他们是怎么实现的呢,那么咱们就看看runtime的源码中,关于associated object是如何实现的吧。ide
首先咱们能够建立一个NSObject的Category,而后动态的添加一个associatedObject的属性:函数
// NSObject+Associated.h
@interface NSObject (Associated)
@property (nonatomic, strong) id associatedObject;
@end
// NSObject+Associated.m
@implementation NSObject (Associated)
@dynamic associatedObject;
+ (void)load {
NSLog(@"bbb...");
}
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, @selector(associatedObject));
}
复制代码
而后我么就能够给associatedObject赋值了:源码分析
NSObject *object = [NSObject new];
object.associatedObject = [NSObject object];
复制代码
这样咱们就完成了一个常见的associated property的调用。ui
咱们看一下,objc_setAssociatedObject都经历了什么吧。atom
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);
/**
// 建立一个符合policy规则的值, 符合的值为:
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1
OBJC_ASSOCIATION_COPY_NONATOMIC = 3
剩下的值都为assign
*/
id new_value = value ? acquireValue(value, policy) : nil;
{
// 建立AssociationsManager对象
AssociationsManager manager;
// 初始化manager中的_map
AssociationsHashMap &associations(manager.associations());
// 对object的地址按位取反, 做为_map的key
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 从_map中找,associtates是否有key值为disguised_object的对象,即ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
// 从ObjectAssociationMap中找key对应的ObjcAssociation,若是有的话,则替换,没有就新建
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 {
// create the new association (first time).
// 将这个类放入_map中,生成一个ObjectAssociationMap, 即_map[disguised_object] = refs
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
// 将key与value对应
(*refs)[key] = ObjcAssociation(policy, new_value);
// 将has_assoc置为true
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// 若是传入的object = nil,则擦除这个key对应的value
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).
// 释放old value
if (old_association.hasValue()) ReleaseValue()(old_association);
}
复制代码
从源码咱们能够看到,void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy)
的流程以下:spa
首先获取判断value是否为空,若是不为空, 那么执行acquireValue方法,获取一个新的值code
而后生成一个AssociationsManager,并生成一个associations,对associtaed property进行全局管理server
而后判断newValue是否为空:对象
最后,release以前的old value继承
从源码咱们能够看到,objc_setAssociatedObject方法仍是比较简单清晰的,就是建立一张全局的关联属性的表,一个对象,对应一个ObjectAssociationMap,而后将传入的key与value传递给他们,固然,须要进行一些判断,好比这个value的policy是什么,传入的值是否为nil等等,可是整体流程仍是很是清晰的。
咱们看这个方法的函数定义以下:
id objc_getAssociatedObject(id object, const void *key)
复制代码
是否是其实就是说,从一个hashMap中,找到key对应的value呢。有了上面的对于objc_setAssociatedObject的分析,咱们直接看源码注释,应该理解上就没什么问题了:
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);
/**
// 建立一个符合policy规则的值, 符合的值为:
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1
OBJC_ASSOCIATION_COPY_NONATOMIC = 3
剩下的值都为assign
*/
id new_value = value ? acquireValue(value, policy) : nil;
{
// 建立AssociationsManager对象
AssociationsManager manager;
// 初始化manager中的_map
AssociationsHashMap &associations(manager.associations());
// 对object的地址按位取反
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 从_map中找,associtates是否有key值为disguised_object的对象,即ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
// 从ObjectAssociationMap中找key对应的ObjcAssociation,若是有的话,则替换,没有就新建
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 {
// create the new association (first time).
// 将这个类放入_map中,生成一个ObjectAssociationMap, 即_map[disguised_object] = refs
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
// 将key与value对应
(*refs)[key] = ObjcAssociation(policy, new_value);
// 将has_assoc置为true
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// 若是传入的object = nil,则擦除这个key对应的value
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).
// 释放old value
if (old_association.hasValue()) ReleaseValue()(old_association);
}
复制代码
在Mattt大神的博客:
中,他对关联属性的使用场景以下:
固然他也提到了,咱们不该该在以下的场景中使用关联属性:
Storing an associated object, when the value is not needed(当这个值再也不须要的时候,咱们不应存储这个关联对象)
Storing an associated object, when the value can be inferred(当这个值能够被推断出来的时候,咱们不应使用关联对象)
Using associated objects instead of X,如:
Subclassing for when inheritance is a more reasonable fit than composition.(当继承比组合更合理的状况使用关联对象)