iOS底層原理之分類關聯對象

準備工做

咱們定義一個LGPerson的類,繼承於NSObject,添加LGPerson的分類LGPerson+LGA,在LGPerson+LGA分類中添加屬性name,實現其setget方法,代碼以下 代碼執行結果以下所示markdown

重點分析

咱們從實現分類屬性的代碼方法中能夠發現setget方法纔是重點,而其中的objc_setAssociatedObjectobjc_getAssociatedObject纔是重中之重。ide

objc_setAssociatedObject分析

咱們從源碼中查找objc_setAssociatedObject函數

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

SetAssocHook.get()至關於接口模式,對外暴露接口,提供給外部使用,咱們繼續查看SetAssocHook從中咱們能夠看到SetAssocHook調用了_base_objc_setAssociatedObject函數,咱們再來查看_base_objc_setAssociatedObject函數_base_objc_setAssociatedObject函數中調用了_object_set_associative_reference函數ui

_object_set_associative_reference分析

源碼this

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // This code used to work when nil was passed for object and key. Some code
    // probably relies on that to not crash. Check and handle it explicitly.
    // rdar://problem/44094390
    if (!object && !value) return;

    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
    //包裝對象
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    //包裝對象(policy,value)
    ObjcAssociation association{policy, value};

    // retain the new value (if any) outside the lock.
    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);

                    }
                }
            }
        }
    }

    // release the old value (outside of the lock).
    association.releaseHeldValue();
}
複製代碼
  • object轉成objc_object對象
  • 裝配對象
  • 咱們看下acquireValue函數根據policy給屬性添加的是copy仍是retain
  • AssociationsManager是個析構函數,能夠聲明多個不是惟一的

  • AssociationsHashMap全局惟一哈希表,咱們從上面能夠看出AssociationsHashMap是經過_mapStorage.get()得到,而_mapStorage是經過static void init()初始化獲得,因爲使用了static,所以AssociationsHashMap全局惟一
  • 查看哈希表是什麼時候賦值的此時associations尚未值;此時打印associations已經由數據了,很明顯是第181行的代碼緣由。
  • 咱們再看try_emplace函數從上圖中咱們能夠得出經過關鍵字key以及桶子TheBucket來查找是否已經在哈希表中存在,若是存在則返回,不然就插入
  • 若是是第一次插入,則設置相關信息設置相關信息
  • 查看LookupBucketFor函數
template<typename LookupKeyT>
  bool LookupBucketFor(const LookupKeyT &Val,
                       const BucketT *&FoundBucket) const {
    const BucketT *BucketsPtr = getBuckets();
    const unsigned NumBuckets = getNumBuckets();

    if (NumBuckets == 0) {
      FoundBucket = nullptr;
      return false;
    }

    // FoundTombstone - Keep track of whether we find a tombstone while probing.
    const BucketT *FoundTombstone = nullptr;
    const KeyT EmptyKey = getEmptyKey();
    const KeyT TombstoneKey = getTombstoneKey();
    assert(!KeyInfoT::isEqual(Val, EmptyKey) &&
           !KeyInfoT::isEqual(Val, TombstoneKey) &&
           "Empty/Tombstone value shouldn't be inserted into map!");

    unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
    unsigned ProbeAmt = 1;
    while (true) {
      const BucketT *ThisBucket = BucketsPtr + BucketNo;
      // Found Val's bucket?  If so, return it.
      if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) {
        FoundBucket = ThisBucket;
        return true;
      }

      // If we found an empty bucket, the key doesn't exist in the set.
      // Insert it and return the default value.
      if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) {
        // If we've already seen a tombstone while probing, fill it in instead
        // of the empty bucket we eventually probed to.
        FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket;
        return false;
      }

      // If this is a tombstone, remember it.  If Val ends up not in the map, we
      // prefer to return it than something that would require more probing.
      // Ditto for zero values.
      if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
          !FoundTombstone)
        FoundTombstone = ThisBucket;  // Remember the first tombstone found.
      if (ValueInfoT::isPurgeable(ThisBucket->getSecond())  &&  !FoundTombstone)
        FoundTombstone = ThisBucket;

      // Otherwise, it's a hash collision or a tombstone, continue quadratic
      // probing.
      if (ProbeAmt > NumBuckets) {
        FatalCorruptHashTables(BucketsPtr, NumBuckets);
      }
      BucketNo += ProbeAmt++;
      BucketNo &= (NumBuckets-1);
    }
  }
複製代碼

整個方法就是TheBucket查找過程,這個過程和cache_t的方法查找類似,都是循環查找,找不到平移知道找到或者查完爲止spa

設置值總結

插入非空值 插入空值 3d

objc_getAssociatedObject分析

id
objc_getAssociatedObject(id object, const void *key)
{
    return _object_get_associative_reference(object, key);
}
複製代碼

objc_getAssociatedObject函數執行了_object_get_associative_referencecode

_object_get_associative_reference

找到則返回當前的值orm

取值總結

總結

相關文章
相關標籤/搜索