iOS底層學習:類的擴展和關聯對象

類的擴展和分類

  1. category:分類、類別
  • 給類增加方法
  • 不能添加成員變量
  • 可以使用runtime給分類添加屬性
  • 分類中添加的屬性指揮生成對應的settergetter方法的聲明,不能生成方法實現和帶下劃線的成員變量。
  1. extension:類擴展
  • 可以添加成員屬性,屬于私有。
  • 可以添加方法,屬于私有。

關于類的擴展,是放在類的聲明之后類的實現之前。類的擴展里面放的多是私有方法和成員變量。類的擴展會在編譯期就作為類的一部分寫入到類信息中。另外,類的擴展只是一個聲明,它依賴主類需要在主類內部完成實現。

關聯對象

分類不能添加屬性,它只是聲明了settergetter方法,沒有實現,那么就需要借助關聯對象來實現這個功能。

分類.h中
@property (nonatomic, copy) NSString *name;

分類.m中
- (void)setCate_name:(NSString *)name{
    /**
     (對象,標識符,value, 策略)
     */
    objc_setAssociatedObject(self, "name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

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

關聯對象源碼探索

關聯對象如何運作的

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

static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};

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

關聯對象設值底層調用的方法就是_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)

_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    ...
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    ObjcAssociation association{policy, value};

    association.acquireValue();
    //工作代碼塊
    {代碼塊}
    association.releaseHeldValue();
}

傳遞進來的object被包裝成了DisguisedPtr,對應的valuepolicy打包成了ObjcAssociation
然后具體的操作放在了代碼塊中

    association.acquireValue();
{
        AssociationsManager manager;
    
        AssociationsHashMap &associations(manager.get());

        if (value) {
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            if (refs_result.second) {
                /* it's the first association we make */
                object->setHasAssociatedObjects();
            }

            /* establish or replace the association */
            auto &refs = refs_result.first->second; // 空的桶子
            auto result = refs.try_emplace(key, std::move(association));
            if (!result.second) {
                association.swap(result.first->second);
            }
        } else {
            auto refs_it = associations.find(disguised);
            if (refs_it != associations.end()) {
                auto &refs = refs_it->second;
                auto it = refs.find(key);
                if (it != refs.end()) {
                    association.swap(it->second);
                    refs.erase(it);
                    if (refs.size() == 0) {
                        associations.erase(refs_it);

                    }
                }
            }
        }
    }
  • acquireValue() 更具傳遞進來的polic對值進行處理
inline void acquireValue() {
        if (_value) {
            switch (_policy & 0xFF) {
            case OBJC_ASSOCIATION_SETTER_RETAIN:
                _value = objc_retain(_value);
                break;
            case OBJC_ASSOCIATION_SETTER_COPY:
                _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                break;
            }
        }
    }

基本流程:
1:創建一個 AssociationsManager 管理類。
2:拿到一張全局唯一的:AssociationsHashMap
3:判斷是否插入的關聯值value是否存在,存在走第4步,不存在則會移除關聯對象。
4:try_emplace,并創建一個空的 ObjectAssociationMap 去取查詢的鍵值對:
5:如果發現沒有這個 key 就插入一個 空的 BucketT進去并返回true
6:通過setHasAssociatedObjects方法標記對象存在關聯對象即置isa指針的has_assoc屬性為true
7:用當前 policy 和 value 組成了一個 ObjcAssociation 替換原來 BucketT 中的空
8:標記一下 ObjectAssociationMap 的第一次為 false

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容