咱們都知道,在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.(當繼承比組合更合理的狀況使用關聯對象)