前言
之前我們聊過了,在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;
};
大家也可以看一下下面這幅圖。

這里很清晰的表明了這幾個(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)上了。就像下圖這樣子。

注意:
通過源碼我們知道,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í)很容易理解。
感謝閱讀。