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

取值总结

总结

相关文章
相关标签/搜索