iOS關聯對象

在iOS開發中若是咱們想給一個對象動態添加屬性或者給category添加屬性的時候,都是經過runtime的關聯對象去實現,那咱們添加的屬性究竟是如何存取的呢?是直接添加到了對象自身的內存中了去嗎?帶着這些疑問讓咱們看一runtime的源碼,解開關聯對象的神祕面紗。ide

關聯對象源碼

存值

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

複製代碼

咱們調用此方法的時候,一共傳遞了四個參數:函數

參數名稱 解釋
id object 須要關聯的對象
void *key 對應的key
id value 對應的值
objc_AssociationPolicy policy 內存管理策略

內存管理策略:ui

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. * The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. * The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object. * The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied. * The association is made atomically. */
};
複製代碼

對於四個參數理解完了以後讓咱們看看它真正的實現函數_object_set_associative_referenceatom

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);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);//獲得對象地址
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);//首先經過對象的地址獲取對象的hashmap
            if (i != associations.end()) {//判斷是否已經存在,已經存在
                // secondary table exists
                ObjectAssociationMap *refs = i->second;//取值,對應的map
                ObjectAssociationMap::iterator j = refs->find(key);//經過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).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {//不存在則建立一個
            // setting the association to nil breaks the association.
            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).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
複製代碼

經過以上代碼咱們能夠看出其實關聯對象在存儲的時候在,生成了一個AssociationsManager單例對象,因此應用中全部的管理對象都存儲於此AssociationsManager中。spa

具體存儲的實現是藉助了C++的關聯容器unordered_map實現的。具體能夠參看代碼中我加的註釋。code

整個過程就是經過object對象的地址存儲了一個相似hashmap的東西;取到此hashmap,而後經過鍵值對的方式將咱們須要存儲的值存儲到此hashmap中,這個過程當中若是有舊值,則最後會將舊值就行釋放對象

取值

取值的過程其實就比較簡單了,就至關於從一個hashmap中取值內存

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;
}
複製代碼

個人博客

個人博客ci

相關文章
相關標籤/搜索