Associated Object 源碼解析

Associated Object 源碼解析

咱們都知道,在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分析

咱們看一下,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是否爲空:對象

    • 若是不爲空,則去associations中查找,是否存在這個object對應的ObjectAssociationMap,
      • 若是沒有的話,則生成一個新的,並將它與object的DISGUISE(object)方法生成的key進行綁定,並將key與生成的ObjcAssociation(policy, new_value)對象進行綁定,而後將object中的has_assoc字段置爲true,表示這個對象已經有了associated object。
      • 若是已經存在的話,那麼直接從_map中讀取這個object對應的全部associated property,而後去查找,這個hashMap中是否存在對應的associated對象,若是存在,則先獲取以前的對象,(由於最後要釋放這個對象),而後賦予它一個新的值。否者直接賦值。
    • 若是爲空的話,那麼則擦除這個key所對應的值,並獲取以前的這個key所對應的value
  • 最後,release以前的old value繼承

從源碼咱們能夠看到,objc_setAssociatedObject方法仍是比較簡單清晰的,就是建立一張全局的關聯屬性的表,一個對象,對應一個ObjectAssociationMap,而後將傳入的key與value傳遞給他們,固然,須要進行一些判斷,好比這個value的policy是什麼,傳入的值是否爲nil等等,可是整體流程仍是很是清晰的。

objc_getAssociatedObject

咱們看這個方法的函數定義以下:

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大神的博客:

Associated Objects

中,他對關聯屬性的使用場景以下:

  1. Adding private variables to facilitate implementation details(添加一些私有的便利的方法實現)
  2. Adding public properties to configure category behavior.(添加一些共有的屬性來配置這個分類)
  3. Creating an associated observer for KVO(在KVO中建立一個關聯的觀察者)

固然他也提到了,咱們不該該在以下的場景中使用關聯屬性:

  1. Storing an associated object, when the value is not needed(當這個值再也不須要的時候,咱們不應存儲這個關聯對象)

  2. Storing an associated object, when the value can be inferred(當這個值能夠被推斷出來的時候,咱們不應使用關聯對象)

  3. Using associated objects instead of X,如:

    Subclassing for when inheritance is a more reasonable fit than composition.(當繼承比組合更合理的狀況使用關聯對象)

相關文章
相關標籤/搜索