iOS底层-类的扩展和关联对象

582 阅读7分钟

前面几篇我们探索了的加载过程,本篇我们研究类相关的两个点:类的扩展关联对象

类扩展

clang编译

@interface JSAnimal : NSObject@property (nonatomic,copy)NSString *name;
​
- (void)sayWow;
​
@end@interface JSAnimal ()@property (nonatomic,copy)NSString *type;
​
- (void)ex_sayWow;
​
@end@implementation JSAnimal
​
+ (void)classMethod{
    NSLog(@"%s",__func__);
}
​
- (void)sayWow{
    NSLog(@"%s",__func__);
}
​
- (void)ex_sayWow{
    NSLog(@"%s",__func__);
}
​
@endint main(int argc, const char * argv[]) {
    @autoreleasepool {
        JSAnimal *animal = [[JSAnimal alloc] init];
        [animal ex_sayWow];
        NSLog(@"done");
    }
    return 0;
}
clang -rewrite-objc main.m -o main.cpp

1626779765218.jpg

1626779854673.jpg 发现扩展里声明的属性方法编译后和中的在一起,作为的一部分,也就是说扩展中的属性和方法编译期就添加到本类中了。

通过源码探索运行时

定义一个JSPerson类和扩展,本类中实现扩展中声明的方法。

@interface JSPerson : NSObject@property (nonatomic, copy) NSString *name;
​
- (void)saySomething;
​
@end#import "JSPerson.h"@implementation JSPerson
​
+ (void)load{
    
}
​
- (void)saySomething{
    NSLog(@"%s",__func__);
}
​
- (void)ext_instanceMethod{
    NSLog(@"%s",__func__);
}
​
+ (void)ext_classMethod{
    NSLog(@"%s",__func__);
}
@end
@interface JSPerson ()
​
- (void)ext_instanceMethod;
​
+ (void)ext_classMethod;
​
@end

注意JSPerson类实现了load方法,目的是让其非懒加载。根据我们的经验,我们在realizeClassWithoutSwift添加断点调试

1626781568401.jpg 通过lldb打印ro中的方法列表

(lldb) p ro
(const class_ro_t *) $0 = 0x0000000100004790
(lldb) p *$0
(const class_ro_t) $1 = {
  flags = 0
  instanceStart = 8
  instanceSize = 16
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "JSPerson" {
      Value = 0x0000000100003b58 "JSPerson"
    }
  }
  baseMethodList = 0x00000001000047d8
  baseProtocols = nil
  ivars = 0x0000000100004840
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100004868
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $1.baseMethods()
(method_list_t *) $2 = 0x00000001000047d8
(lldb) p *$2
(method_list_t) $3 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 4)
}
(lldb) p $3.get(0).big()
(method_t::big) $4 = {
  name = "saySomething"
  types = 0x0000000100003d84 "v16@0:8"
  imp = 0x0000000100003600 (KCObjcBuild`-[JSPerson saySomething])
}
(lldb) p $3.get(1).big()
(method_t::big) $5 = {
  name = "ext_instanceMethod"
  types = 0x0000000100003d84 "v16@0:8"
  imp = 0x0000000100003630 (KCObjcBuild`-[JSPerson ext_instanceMethod])
}
(lldb) p $3.get(2).big()
(method_t::big) $6 = {
  name = "name"
  types = 0x0000000100003d98 "@16@0:8"
  imp = 0x0000000100003660 (KCObjcBuild`-[JSPerson name])
}
(lldb) p $3.get(3).big()
(method_t::big) $7 = {
  name = "setName:"
  types = 0x0000000100003da0 "v24@0:8@16"
  imp = 0x0000000100003690 (KCObjcBuild`-[JSPerson setName:])
}

可以看到,扩展中的方向现在已经加载了已知ro中的方法是编译期就确定的,所以也验证了扩展的方法是在编译期添加到本类的。

小结

  • 类的扩展 在编译期 会作为类的一部分,和类一起编译进来
  • 类的扩展只是 声明 ,依赖于 本类 的实现。

分类的关联对象

我们知道分类正常是不能添加属性的,但是通过关联对象可以,其实现通过两个方法

  • 通过objc_setAssociatedObject方法设置值。
  • 通过objc_getAssociatedObject方法取值。

下面我们分别探索。

objc_setAssociatedObject流程

objc_setAssociatedObject有四个参数:

  • 参数1:要关联的对象
  • 参数2:表示符,方便查找识别
  • 参数3:value值
  • 参数4:属性的策略,我们定义属性经常用到的如nonatomicstrongweak

首先定义JSPerson分类,定义一个属性cateegory_name

@interface JSPerson (JSCategory)
​
@property (nonatomic, copy) NSString *cateegory_name;
​
@end
@implementation JSPerson (JSCategory)
​
- (void)setCateegory_name:(NSString *)category_name{
    objc_setAssociatedObject(self, "category_name", category_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
​
- (NSString *)category_name{
    return  objc_getAssociatedObject(self, "category_name");
}
​
@end

main函数赋值属性的地方添加断点,根据调用情况

1626783154111.jpg

定位到objc_setAssociatedObject方法

1626783226697.jpg

调用的是_object_set_associative_reference方法,我们继续跟进查看源码:

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));
    ///将object封装一下 类型为DisguisedPtr
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    ///包装policy  value
    ObjcAssociation association{policy, value};
​
    // retain the new value (if any) outside the lock.
    //根据策略类型(strong、weak等)进行处理
    association.acquireValue();
​
    bool isFirstAssociation = false;
    {
        //初始化manager变量,相当于自动调用AssociationsManager的构造函数进行初始化
        AssociationsManager manager;
        ///一个HashMap
        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 */
                isFirstAssociation = true;
            }
​
            /* establish or replace the association 建立或者替换关联*/
            auto &refs = refs_result.first->second;///得到一个空的桶,找到引用对象类型,即第一个元素的second值
            auto result = refs.try_emplace(key, std::move(association));//查找当前的key是否有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);
​
                    }
                }
            }
        }
    }
    // Call setHasAssociatedObjects outside the lock, since this
    // will call the object's _noteAssociatedObjects method if it
    // has one, and this may trigger +initialize which might do
    // arbitrary stuff, including setting more associated objects.
    if (isFirstAssociation)
        object->setHasAssociatedObjects();
    // release the old value (outside of the lock).释放老的关联值
    association.releaseHeldValue();
}
​

通过源码我们看到大体的流程为:

  • 创建一个AssociationsManager管理类

  • 获取静态哈希表:associations

  • 判断关联值value是否为空

    • 如果为空就走:插入空值流程。
    • 如果不为空继续下一步
  • 通过try_emplace方法,创建一个空的 ObjectAssociationMap 去取查询的键值对

  • 如果发现没有 key插入一个 空的 BucketT进去并返回true

  • 通过setHasAssociatedObjects方法标记对象存在关联对象

  • 用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT中的值

  • 标记一下 ObjectAssociationMap第一次false

    源码调试

    对流程有了大概的认识,我们开始断点调试

if (value) 之前变量的值

1626785173962.jpg

通过lldb我们打印出了disguisedassociationmanagerassociationsvalue的值

(lldb) p disguised
(DisguisedPtr<objc_object>) $0 = (value = 18446744069393517536)
(lldb) p association
(objc::ObjcAssociation) $1 = {
  _policy = 3
  _value = 0x0000000100004080 "哈哈哈"
}
(lldb) p manager
(objc::AssociationsManager) $2 = {}
(lldb) p associations
(objc::AssociationsHashMap) $3 = {
  Buckets = nil
  NumEntries = 0
  NumTombstones = 0
  NumBuckets = 0
}
(lldb) p value
(__NSCFConstantString *) $4 = 0x0000000100004080 "哈哈哈"
value不为空流程

上面我们看到value值不为空,我们进入if语句继续调试。

  • p refs_result

    (lldb) p refs_result
    (std::pair<objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false>, bool>) $5 = {
      first = {
        Ptr = 0x00000001012102a0
        End = 0x0000000101210300
      }
      second = true
    }
    

    看到refs_result的数据结构看起来比较复杂,但是值比较简单,有两个属性firstsecond。其中first的值为:

(lldb) p $5.first.Ptr
(objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false>::pointer) $6 = 0x00000001012102a0
(lldb) p $5.first.End
(objc::DenseMapIterator<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, false>::pointer) $7 = 0x0000000101210300

second值为true,所以会执行isFirstAssociation = true

  • try_emplace方法,associations调用了try_emplace方法,我们看一下他的源码
  template <typename... Ts>
  std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
    BucketT *TheBucket;
    ///根据key找桶
    if (LookupBucketFor(Key, TheBucket))
      ///如果桶存在 则返回
      return std::make_pair(
               makeIterator(TheBucket, getBucketsEnd(), true),
               false); // Already in map.
​
    // Otherwise, insert the new element.
    ///如果不存在则插入桶 并返回
    TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
    return std::make_pair(
             makeIterator(TheBucket, getBucketsEnd(), true),
             true);
  }
  1. 通过LookupBucketFor方法查找桶,如果map中已经存在,则直接返回,其中make_pair的第二个参数bool值为false
  2. 如果没有找到,则通过InsertIntoBucket插入map,其中make_pair的第二个参数bool值为true 我们断点进来使用lldb调试

1626787367239.jpg `p TheBucket

(lldb) p TheBucket
(objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > *) $1 = 0x0000000101c04200
(lldb) p *$1
(objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >) $2 = {
  std::__1::pair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > = {
    first = (value = 18446744069384153152)
    second = {
      Buckets = nil
      NumEntries = 0
      NumTombstones = 0
      NumBuckets = 0
    }
  }
}

看到TheBucket的类型与 refs_result中属性的类型是一致的。

  • LookupBucketFor方法

我们进入LookupBucketFor源码发现有两个实现,它们的区别是FoundBucket的参数类型第一个实现多const修饰。

1626786329854.jpg

我们通过断点调试,发现调用的是第2个实现,第2个方法内部调用了第1个实现。我们先看第1个实现源码,注释中有流程说明。

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!");
    ///通过哈希函数得到BucketNo
    unsigned BucketNo = getHashValue(Val) & (NumBuckets-1);
    unsigned ProbeAmt = 1;
    while (true) {//与catche_t查找imp类似,通过哈希查找
      const BucketT *ThisBucket = BucketsPtr + BucketNo;
      // Found Val's bucket?  If so, return it. 如果找到直接返回 true
      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.
      //如果是一个空桶 说明key不在集合中,将key插入 返回false
      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.
      // 以上条件都不满足 BucketNo调整进行平移、再哈希继续查找
      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);
    }
  }

第2个LookupBucketFor的实现

  •   bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) {
        const BucketT *ConstFoundBucket;//空的桶
        bool Result = const_cast<const DenseMapBase *>(this)
          ->LookupBucketFor(Val, ConstFoundBucket);//调用第一个LookupBucketFor方法查找
        FoundBucket = const_cast<BucketT *>(ConstFoundBucket);//如果找到复制给第二个参数,因为第二个参数是引用类型会直接让调用的地方获取到值。也就是try_emplace方法的TheBucket
        return Result;
      }
    
  • 继续走valuetrue的流程

    后面还会执行try_emplace方法,我们在执行之前查看一下refs的值

    (lldb) p refs
    (objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $3 = {
      Buckets = nil
      NumEntries = 0
      NumTombstones = 0
      NumBuckets = 0
    }
    

    try_emplace方法之后refs的值

    (lldb) p refs
    (objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $4 = {
      Buckets = 0x0000000100711390
      NumEntries = 1
      NumTombstones = 0
      NumBuckets = 4
    }
    

    第一次执行try_emplace插入的是一个空桶,还没有值,第二次执行第一次执行try_emplace才插入值,即往空桶中插入ObjectAssociationMap(value,policy),返回true。

    此时result.secondtrue,此时属性的value就关联上了。

关联对象结构

关联对象的设置图如下:

关联对象.jpg

属性设计的哈希表结构如下:

关联对象哈希表.jpg

map中有很多的关联对象map,类型是ObjectAssociationMap,其中key为DisguisedPtr<objc_object>,例如JSPerson会对应一个ObjectAssociationMapJSTeacher也会对应一个ObjectAssociationMap

ObjectAssociationMap哈希表中有很多key-value键值对,其中key的类型为const void *,其实这个key从底层这个方法_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)的参数就可以看出,key是我们关联属性时设置的字符串value的类型为ObjcAssociation

value为空流程

这个过程其实就是else流程,也就是我们对value设置为nil的流程,主要就是移除关联。

 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);
​
              }
          }
      }
 }
  • 根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
  • 清理迭代器
  • 实际上如果插入空置 相当于清除

objc_getAssociatedObject流程

main方法中添加一个取值的语句
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        JSPerson *person = [[JSPerson alloc] init];
        person.category_name = @"哈哈哈";
        NSString *name = person.category_name;
        NSLog(@"done");
    }
    return 0;
}
objc_getAssociatedObject源码实现
id
objc_getAssociatedObject(id object, const void *key)
{
    return _object_get_associative_reference(object, key);
}

调用_object_get_associative_reference函数。

_object_get_associative_reference源码
id
_object_get_associative_reference(id object, const void *key)
{
    ObjcAssociation association{};
​
    {
        ///创建AssociationsManager管理类
        AssociationsManager manager;
        ///获取静态哈希表
        AssociationsHashMap &associations(manager.get());
        /////找到迭代器,即获取buckets
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {//如果这个迭代查询器不是最后一个 继续获取
            //找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
            ObjectAssociationMap &refs = i->second;
            //根据key查找ObjectAssociationMap,即获取bucket
            ObjectAssociationMap::iterator j = refs.find(key);
            if (j != refs.end()) {
                //获取ObjcAssociation
                association = j->second;
                association.retainReturnedValue();
            }
        }
    }
    ///返回值
    return association.autoreleaseReturnedValue();
}

看源码分析主要分为以下几步

  • 创建一个 AssociationsManager 管理类

  • 获取静态哈希表:AssociationsHashMap

  • 通过find方法根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器

  • 如果这个迭代查询器不是最后一个 继续获取 : ObjectAssociationMap (policy和value)

  • 通过find方法找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value

  • 返回 value

    查找方法find
      iterator find(const_arg_type_t<KeyT> Val) {     
        BucketT *TheBucket;    
        if (LookupBucketFor(Val, TheBucket))       
           return makeIterator(TheBucket, getBucketsEnd(), true);    
        return end();   
      }
    
    通过源码看取值流程

    我们直接断点到_object_get_associative_reference函数

1626789232319.jpg 执行p ip i->second:

```
(lldb) p i 
(objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > >, DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >, objc::DenseMapInfo<DisguisedPtr<objc_object> >, objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > > >::iterator) $0 = {   Ptr = 0x0000000100631d60   End = 0x0000000100631d80 } (lldb) 
p i->second 
(objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >) $1 = {   Buckets = 0x0000000100631d80   NumEntries = 1   NumTombstones = 0   NumBuckets = 4 }
```

再次执行find方法,在调用find方法之前,我们先打印j,此时valuenil

(lldb) p j
(objc::DenseMapBase<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >::iterator) $2 = {
  Ptr = 0x00007ffeefbff400
  End = 0x00000001002e70db
}
(lldb) p j->second
(objc::ObjcAssociation) $3 = {
  _policy = 4294980472
  _value = nil
}
(lldb) 

执行find方法之后再次打印,发现value已经有值,也就是取到了关联对象。

(lldb) p j
(objc::DenseMapBase<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >, const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >::iterator) $4 = {
  Ptr = 0x0000000100631d80
  End = 0x0000000100631de0
}
(lldb) p j->second
(objc::ObjcAssociation) $5 = {
  _policy = 3
  _value = 0x0000000100004080 "哈哈哈"
}

总结

本篇主要探索了扩展关联对象,其中类的扩展编译期 会作为类的一部分,和类一起编译进来。

关联对象设置流程为:

  • 创建一个AssociationsManager管理类

  • 获取静态哈希表:associations

  • 判断关联值value是否为空

    • 如果为空就走:插入空值流程。
    • 如果不为空继续下一步
  • 通过try_emplace方法,创建一个空的 ObjectAssociationMap 去取查询的键值对

  • 如果发现没有 key插入一个 空的 BucketT进去并返回true

  • 通过setHasAssociatedObjects方法标记对象存在关联对象

  • 用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT中的值

  • 标记一下 ObjectAssociationMap第一次false

    关联对象取值的流程为:

    • 创建一个 AssociationsManager 管理类
    • 获取静态哈希表:AssociationsHashMap
    • 通过find方法根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
    • 如果这个迭代查询器不是最后一个 继续获取 : ObjectAssociationMap (policy和value)
    • 通过find方法找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
    • 返回 value