OC底层原理探索之分类、类扩展、关联对象

449 阅读3分钟

methodList数据结构

realizeClassWithoutSwift -> methodizeClass(Attach categories)

// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();

image.png 通常查看里面的具体内容使用的lldb调试命令是**p $2.get(0).big()**,查看get函数

    Element& getOrEnd(uint32_t i) const { 
        ASSERT(i <= count);
        return *PointerModifier::modify(*this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));
    }
    Element& get(uint32_t i) const { 
        ASSERT(i < count);
        return getOrEnd(i);
    }

所以能得出method_list_t里面存储的是指针

分类加载是否需要排序

由上面我们知道methods是一个指针数据,指针本身并没有排序的说法。所以分类本身并不需要排序

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

Exention vs Category

category:

  • 专门给类添加新的方法
  • 不能给类添加成员属性,添加了成员变量也无法获取到
  • 可以通过runtime给类添加属性
  • 分类中用property修饰的属性,只会生成getter和setter的申明,不能生成带_的成员变量和方法实现

extention:

  • 可以说是特殊的分类,匿名分类
  • 可以给类添加成员属性但是私有变量
  • 可以给类添加方法也是私有方法

类扩展

在main中写一个类扩展。clang一下找到main.cpp文件打开。在主类的方法列表里面找到了类扩展里面的方法和set/get方法。

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[18];
} _OBJC_$_INSTANCE_METHODS_LGStudent __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	18,
	{(struct objc_selector *)"ext_instanceMethod", "v16@0:8", (void *)_I_LGStudent_ext_instanceMethod},
	{(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LGStudent_ext_name},
	{(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_LGStudent_setExt_name_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGStudent_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGStudent_setName_},
	{(struct objc_selector *)"age", "i16@0:8", (void *)_I_LGStudent_age},
	{(struct objc_selector *)"setAge:", "v20@0:8i16", (void *)_I_LGStudent_setAge_},
	{(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LGStudent_ext_name}}
}

我们知道,分类可以影响主类的加载流程,那么类扩展呢,同样在源码realizeClassWithoutSwift处调试 image.png 不难得出,扩展的方法已经添加到方法列表里面去了。

分类

我们在分类中添加属性,会报出这样的警告,需要我们手动的实现set和get方法 image.png

- (void)setCate_name:(NSString *)cate_name{
    /**
     1: 对象
     2: 标识符
     3: value
     4: 策略
     */
    objc_setAssociatedObject(self, "cate_name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

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

关联对象objc_setAssociatedObject设值

objc_setAssociatedObject -> _object_set_associative_reference 关联的对象的本质是存储这个属性

bool isFirstAssociation = false;
    {
        // 1.创建一个AssociationsManager管理类
        AssociationsManager manager;
        // 2.获取唯一的全局静态哈希map
        AssociationsHashMap &associations(manager.get());
        // 3.判断插入的关键值是否存在 比如”name“
        if (value) {
            // 4.创建一个空的 ObjectAssociationMap去获取查询的键值对
            // 5.try_emplace 如果没有发现这个key(name)就创建一个空的TheBucket进去 然后返回
            // 6.try_emplace 标记对象存在关联对象
            // refs_result的数据结构 first{Ptr,End}  second = true
            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 */
            /* refs_result.first->second {
                    Buckets = nil
                    NumEntries = 0
                    NumTombstones = 0
                    NumBuckets = 0
               } */
            auto &refs = refs_result.first->second;
            // 7.try_emplace 用当前association(策略和value)组成的ObjectAssociation替换原来BucketT的空置
            // 8.try_emplace 标记ObjectAssociationMap的first=false
            auto result = refs.try_emplace(key, std::move(association));
            /* refs_result.first->second {
                Buckets = 0x0000000101313590
                NumEntries = 1
                NumTombstones = 0
                NumBuckets = 4
               } */
            // 前后对比发现buckets已经赋值上了
            if (!result.second) {
                association.swap(result.first->second);
            }
        } else {
            // 4.根据DisguisedPtr找到AssociationsHashMap中的迭代查询器
            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);
                    //5.清理
                    refs.erase(it);
                    //6.如果插入为空 也清理
                    if (refs.size() == 0) {
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }

LookupBucketFor算法

try_emplace -> LookupBucketFor与寻找cache里面的bucket算法一致

template<typename LookupKeyT>
  bool LookupBucketFor(const LookupKeyT &Val,
                       const BucketT *&FoundBucket) const {
    // ...
	// 找到bucket的index
    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;
      }

      //如果找到一个空的bucket,则该键不存在于集合中。插入它并返回默认值
      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 (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) &&
          !FoundTombstone)
        FoundTombstone = ThisBucket;  // Remember the first tombstone found.
      if (ValueInfoT::isPurgeable(ThisBucket->getSecond())  &&  !FoundTombstone)
        FoundTombstone = ThisBucket;

      if (ProbeAmt > NumBuckets) {
        FatalCorruptHashTables(BucketsPtr, NumBuckets);
      }
      BucketNo += ProbeAmt++;
      BucketNo &= (NumBuckets-1); // 再哈希
    }
  }

关联对象objc_getAssociatedObject取值

objc_getAssociatedObject -> _object_get_associative_reference

    {
        //1.创建一个AssociationsManager管理类
        AssociationsManager manager;
        //2.获取唯一的全局静态哈希map
        AssociationsHashMap &associations(manager.get());
        //3.根据DisguisedPtr找到AssociationsHashMap中的迭代查询器
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        /*
         associations.end() = {
                    Ptr = 0x0000000100673cc0
                    End = 0x0000000100673cc0
         }
         */
        if (i != associations.end()) {
            /*
             i->second = {
                        Buckets = 0x0000000101b3c4f0
                        NumEntries = 1
                        NumTombstones = 0
                        NumBuckets = 4
                }
             */
            //4.如果这个迭代查询器不是最后一个获取ObjectAssociationMap
            ObjectAssociationMap &refs = i->second;
            // 5.找到ObjectAssociationMap的迭代查询器
            ObjectAssociationMap::iterator j = refs.find(key);
            if (j != refs.end()) {
                association = j->second;
                // 6.返回经过属性修饰符修饰的value
                association.retainReturnedValue();
            }
        }
    }

总结

其实就是两层哈希map,存取的时候两层处理(类似于二维数组) image.png

补充

构造函数和析构函数

class AssociationsManager {
    using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
    static Storage _mapStorage;

public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }

    AssociationsHashMap &get() {
        return _mapStorage.get();
    }

    static void init() {
        _mapStorage.init();
    }
};

AssociationsManager manager就是调用了构造函数AssociationsManager() 出了作用域自动调用析构函数~AssociationsManager()