OC底层原理14之类扩展与关联对象

1,021 阅读3分钟

本章内容

  1. 类扩展
  2. 关联对象的实现原理

本章目的

  1. 很多人不知道什么是类扩展,其实你一直在用
  2. 知道关联对象的实现原理

类扩展

类别我相信很多人都很清楚了,关于类别的加载也已经探讨过。但是面试的时候很多面试官都会问,扩展与类别的区别。扩展其实我们一直在用:它在声明之后,实现之前。标红的就是扩展。

image.png

clang还原查看扩展

可以看到其成员,属性都加到类中了。额外说一句,如果说类扩展有方法声明的话而且实现了则也会加到类的方法列表中。而且也已经在ro中了,已经测试过了,就不展示了。

image.png

分类与扩展

只提一个两者比较醒目的区别,其他的自己可以联想一下

分类: 分类中添加属性,只会生成成员变量的setter,getter方法的声明,没有进行方法的实现和成员变量。

扩展:扩展中添加属性,就只是私有变量,添加方法也是私有方法。

关联对象

请记得,分类中可以添加属性,但是添加的属性有问题(只有方法声明,没有成员变量与方法实现)。对于关联策略有那么几种,自行看。其实关于api一直不变,但是objc的源码调用方法其实变过几次。看就看最新的,我看的版本它调用的函数是_object_set_associative_reference

其实关联对象的实现方式就是一张大的哈希map表也就是关联的总表。这个表包含了一个个以key-value形式的哈希map表。

大表就是 AssociationsHashMap {DisguisedPtr<objc_object> : ObjectAssociationMap}。全局唯一一张,这个对象的小表

小表就是 ObjectAssociationMap {const void * : ObjcAssociation}。代表这个对象所有的关联对象

image.png

源码分析

先提示一下,关联对象的生命周期是什么?其实它是跟随其对象的生命周期走的。

记得之前有版本走的是setHook?没关系不必关心 image.png

_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<objc_object> disguised{(objc_object *)object};
    // 这个是关联的值可策略,它相当于第二张表的内容
    ObjcAssociation association{policy, value};

    // retain the new value (if any) outside the lock.
    //该方法就是对关联策略进行一些处理。OBJC_ASSOCIATION_SETTER_RETAIN和OBJC_ASSOCIATION_SETTER_COPY
    association.acquireValue();

    bool isFirstAssociation = false;
    {
        // 这个没什么就是创建一个对象
        AssociationsManager manager;
        // 获取全局唯一一张关联表。哈希map表
        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;
            // 这时候就是去小表中了,跟大表操作一样,进行替换插入,但是映射没有改变
            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);

                    }
                }
            }
        }
    }

    // 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();
}

_object_get_associative_reference

取值的函数,没什么看的,就是从表里面取值

id
_object_get_associative_reference(id object, const void *key)
{
    ObjcAssociation association{};
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {
            ObjectAssociationMap &refs = i->second;
            ObjectAssociationMap::iterator j = refs.find(key);
            if (j != refs.end()) {
                association = j->second;
                association.retainReturnedValue();
            }
        }
    }

    return association.autoreleaseReturnedValue();
}