iOS底層系列:關(guān)聯(lián)對(duì)象

前言

之前我們聊過了,在Category中聲明一個(gè)屬性,可以自己手動(dòng)實(shí)現(xiàn)set和get方法,但是因?yàn)闆]有成員變量,所以說并不能儲(chǔ)值。

我們可以通過runtime的api實(shí)現(xiàn)讓成員變量可以儲(chǔ)值,其實(shí)本質(zhì)也并不是儲(chǔ)存,而是通過關(guān)聯(lián)對(duì)象實(shí)現(xiàn)了這種看似是可以儲(chǔ)值的效果。

我們可以用下面的方法來設(shè)置關(guān)聯(lián)對(duì)象。

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);

今天我們就來探究一下這種關(guān)聯(lián)對(duì)象的實(shí)現(xiàn)邏輯。

實(shí)現(xiàn)

我們?cè)趓untime源碼中搜索objc_setAssociatedObject,最終可以定位到下面這個(gè)方法。

_object_set_associative_reference

通過簡單的分析源碼,我們可以看出關(guān)聯(lián)對(duì)象的實(shí)現(xiàn),大致是由下面四個(gè)類結(jié)合實(shí)現(xiàn)的。

AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation

簡單的抽取和簡化一下源碼,基本可以得出這四個(gè)類的關(guān)系。

class AssociationsManager {
    static AssociationsHashMap * _map
}

typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;

typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
};

大家也可以看一下下面這幅圖。

association_1.png

這里很清晰的表明了這幾個(gè)類的關(guān)系,AssociationsManager有一個(gè)AssociationsHashMap類型的屬性_map,_map的key是DisguisedPtr<objc_object>類型,value是ObjectAssociationMap類型,而這個(gè)ObjectAssociationMap類型中的key是一個(gè)指針(void*),value是ObjcAssociation類型。這個(gè)ObjcAssociation中有兩個(gè)重要的是就是屬性,_policy和_value。

關(guān)聯(lián)對(duì)象的設(shè)置就是通過這幾個(gè)類來實(shí)現(xiàn)的,上面我們也分析完了這幾個(gè)類的相互關(guān)系,那這些類和我們?cè)谡{(diào)用objc_setAssociatedObject時(shí)傳入的參數(shù)關(guān)系是怎么樣的呢?我們繼續(xù)分析源碼。

我們?cè)诳匆槐橄旅孢@方法。

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

在調(diào)用的時(shí)候我們傳入了四個(gè)參數(shù),第一個(gè)就是我們的被關(guān)聯(lián)對(duì)象,第二個(gè)是我們?cè)O(shè)置的關(guān)聯(lián)對(duì)象的key, 第三個(gè)就是我們要存的值,第四個(gè)就是關(guān)聯(lián)的策略(類似retain,copy等)

這里先直接說結(jié)果吧,其實(shí)可以理解為AssociationsManager的屬性_map中,以參數(shù)object為key,value是一個(gè)ObjectAssociationMap類型的map。ObjectAssociationMap中,則是以參數(shù)中的key為key,value是一個(gè)ObjcAssociation類型的對(duì)象,最后我們參數(shù)中的value和policy就儲(chǔ)存在這個(gè)ObjectAssociation變量中。

好了,這樣我們傳的四個(gè)參就跟這些類對(duì)應(yīng)上了。就像下圖這樣子。

association_2.png

注意:

通過源碼我們知道,AssociationsHashMap中的key并不是使用直接使用了object,而是一個(gè)DisguisedPtr類型,但是通過源碼我們可以看到DisguisedPtr<objc_object> disguised{(objc_object *)object};,說到底這個(gè)key也是根據(jù)我們的object來生成的,所以可以說這個(gè)key與我們的object是對(duì)應(yīng)關(guān)系,從而可以理解為是這個(gè)object為key。

不得不說,蘋果的設(shè)計(jì)還是很巧妙的,每一個(gè)要設(shè)置關(guān)聯(lián)對(duì)象的對(duì)象對(duì)應(yīng)一個(gè)map,在這個(gè)map中,使用我們自己設(shè)置的不同的關(guān)聯(lián)對(duì)象的key為key,用關(guān)聯(lián)對(duì)象的value和策略生成一個(gè)ObjcAssociation類型的對(duì)象為value,然后進(jìn)行儲(chǔ)存,這樣每一個(gè)要設(shè)置關(guān)聯(lián)對(duì)象的對(duì)象,具體的每一個(gè)關(guān)聯(lián)對(duì)象,都能一一對(duì)應(yīng)起來了。

上面的話說的有點(diǎn)拗口,相信大家可以理解。

總結(jié)和補(bǔ)充

關(guān)聯(lián)對(duì)象并不是儲(chǔ)存在被關(guān)聯(lián)對(duì)象本身的,而是儲(chǔ)存在全局的統(tǒng)一的一個(gè)AssociationsHashMap中。

從源碼中我們還可以看出如果我們給關(guān)聯(lián)對(duì)象設(shè)置nil,則代表移除該關(guān)聯(lián)對(duì)象。

同時(shí)還要注意,因?yàn)槲覀兊膐bject其實(shí)是一個(gè)對(duì)象,這就要涉及內(nèi)存管理問題,當(dāng)我們的這個(gè)object釋放后,其實(shí)整個(gè)AssociationsHashMap中其對(duì)應(yīng)項(xiàng)都會(huì)被移除,這個(gè)以后我們討論內(nèi)存管理的時(shí)候再說。

objc_getAssociatedObject實(shí)現(xiàn)咱們就不具體分析了,如果看動(dòng)了set的實(shí)現(xiàn),get其實(shí)很容易理解。

感謝閱讀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容