iOS分類添加成員變量和關(guān)聯(lián)對(duì)象詳解

一、背景

因?yàn)榉诸惖牡讓咏Y(jié)構(gòu)不能添加成員變量,要想實(shí)現(xiàn)成員變量的效果,可以使用runtime關(guān)聯(lián)對(duì)象去實(shí)現(xiàn)


二、分類不能添加成員變量的原因

  • 有個(gè)問題需要注意下,分類是能聲明屬性,但是不能添加成員變量,這個(gè)很容易混淆。
    \color{red}{這里或許有疑問,聲明屬性不是能自動(dòng)生成成員變量嗎?}
    但是在分類里是不一樣的,在聲明屬性在分類里,不會(huì)自動(dòng)添加成員變量,也不會(huì)自動(dòng)實(shí)現(xiàn)set和get方法。
    \color{red}{這里又會(huì)有童鞋說了,那我們自己去添加成員變量和實(shí)現(xiàn)set和get方法不就可以了}
    在分類中添加成員變量會(huì)報(bào)Instance variables may not be placed in categories實(shí)例變量不能放在類別中的錯(cuò)誤

    1-1.png

  • 分類底層結(jié)構(gòu)

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

從分類的底層結(jié)構(gòu)中可以看到,底層結(jié)構(gòu)中有存放實(shí)例方法、類方法、協(xié)議和屬性的地方,但是沒有存放成員變量的地方,所以從底層結(jié)構(gòu)上就決定了無法添加成員變量。


三、runtime關(guān)聯(lián)對(duì)象實(shí)現(xiàn)分類添加成員變量的效果

  • 關(guān)聯(lián)對(duì)象API
    添加關(guān)聯(lián)對(duì)象
void objc_setAssociatedObject(id object, const void * key,
                                    id value, objc_AssociationPolicy policy)

第一個(gè)參數(shù):填為哪個(gè)對(duì)象去添加關(guān)聯(lián)對(duì)象的(一般填self)
第二個(gè)參數(shù):關(guān)聯(lián)對(duì)象的key,后面獲取關(guān)聯(lián)對(duì)象就是根據(jù)這個(gè)key去獲取的
第三個(gè)參數(shù):關(guān)聯(lián)對(duì)象的值
第四個(gè)參數(shù):關(guān)聯(lián)對(duì)象策略

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, // assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // strong, nonatomic
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // copy, nonatomic
OBJC_ASSOCIATION_RETAIN = 01401, // strong, atomic
OBJC_ASSOCIATION_COPY = 01403 // copy, atomic
};

  • 獲取關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject(id object, const void * key)

第一個(gè)參數(shù):需要關(guān)聯(lián)對(duì)象的對(duì)象
第二個(gè)參數(shù):關(guān)聯(lián)對(duì)象的key,跟添加關(guān)聯(lián)對(duì)象時(shí)一樣的

  • 移除關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects(id object)

參數(shù):需要關(guān)聯(lián)對(duì)象的對(duì)象

  • 實(shí)現(xiàn)關(guān)聯(lián)對(duì)象添加成員變量效果
    1、先在分類中添加頭文件#import <objc/runtime.h>
    2、在分類中聲明屬性
    3、在分類中實(shí)現(xiàn)set和get方法
- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, @selector(name));
}

這樣就能實(shí)現(xiàn)為分類添加成員變量的效果了


四、關(guān)聯(lián)對(duì)象原理

  • 關(guān)聯(lián)對(duì)象存放在哪
    關(guān)聯(lián)對(duì)象并不是像本類的成員變量保存在本類中,因?yàn)榉诸惖讓咏Y(jié)構(gòu)中沒有存放的地方,關(guān)聯(lián)對(duì)象是存放在全局的AssociationsManager中。
  • AssociationsManager中有個(gè)AssociationsHashMap類似于字典一樣的結(jié)構(gòu)
class AssociationsManager {
    // associative references: object pointer -> PtrPtrHashMap.
    static AssociationsHashMap *_map;
public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }
    
    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};
  • AssociationsHashMap中以需要關(guān)聯(lián)對(duì)象的對(duì)象為key(關(guān)聯(lián)對(duì)象的一個(gè)參數(shù)),保存中這個(gè)對(duì)象的所有被關(guān)聯(lián)的屬性,value為ObjectAssociationMap
    class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };
  • ObjectAssociationMap中是已添加關(guān)聯(lián)對(duì)象的key值作為key值的,value為ObjcAssociation,而ObjcAssociation里是包含了關(guān)聯(lián)對(duì)象的值和關(guān)聯(lián)策略
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>
1-2.png
?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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