OC底层探索(十八): 类扩展和关联对象

2022-05-27  本文已影响0人  ShawnAlex

所用版本:

先看下分类和类扩展区别

分类category
扩展extension

其实我们常用的@interface XXX()就是类扩展, 没想到吧, 用的频率比分类都多 :)。

同时留意下:

错误写法1 错误写法2

类扩展extension

现在在类扩展中加个属性, 方法

例子

Clang一下看下底层内部实现

// 命令
clang -rewrite-objc main.m -o main.cpp
cpp

可看到, 扩展 方法和属性的set, get 方法就直接添加到 method_list中,作为类的一部分。其实类扩展是在编译时直接添加到本类里面, 与分类不同, 分类会生成一个category_t 结构体会影响主类, 但是类扩展不会。我们可以在realizeClassWithoutSwift中读一下ro验证下:

调用

留意下: 要先有个调用, 不然不会加载. 挺好的, 省着浪费内存空间

验证

可发现类扩展方法已经在ro中, 从而也可验证类扩展直接添加到本类中



当然类扩展也可以单独创建, 看下extension创建

extension 创建

Objctive-C FileFile Type 选择Extension即可

实现

但是只会生成一个.h 不会生成.m, 实现还是要再本类.m中进行

总结:


关联对象

错误例子 错误例子

之前写分类也说过, 分类是不能添加属性的(只声明 get & setter无法存取), 但是可以通过关联对象方法添加属性。关键方法:

先看下效果:


例子 例子 例子

可看到能给属性正常赋值, 看一下底层

// 第一个参数: 对象
// 第二个参数: 标识符
// 第三个参数: value
// 第四个参数: 策略

void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
    _object_set_associative_reference(object, key, value, policy);
}

其中:
第一个参数::要关联的对象,即给谁添加关联属性
第二个参数:标识符,方便下次查找
第三个参数:value
第四个参数:属性的策略,nonatomic, atomic, retain, copy 等

属性策略
/**********************************************************************
* Associative Reference Support
**********************************************************************/

id
objc_getAssociatedObject(id object, const void *key)
{
    return _object_get_associative_reference(object, key);
}

其实以前版本与现在版本底层变化很大, 但是 objc_setAssociatedObject, objc_getAssociatedObject名字不会变, 即API稳定

_object_set_associative_reference

看下_object_set_associative_reference内部实现, 看下关联对象做了哪些事情

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // 如果object &&  value 直接return 话说你不调不就行了么 :)
    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
    //  因为传入的可能是: 类名1, 类名2, 类名3 ... 转成一个统一类型方便后面处理
    //  disguised 方法是对ptr 一个处理
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
 
    // 面向对象的处理
    // 包装 {policy, value} 为 ObjcAssociation
    ObjcAssociation association{policy, value};

    bool isFirstAssociation = false;
    {
        // 定义一个构造函数manager, 非单例
        AssociationsManager manager;
        // AssociationsHashMap 是单例,通过 AssociationsManager 获取 AssociationsHashMap。它是在`map_images`的时候初始化。
        AssociationsHashMap &associations(manager.get());

        // 传入的value
        if (value) {
            // 如果 value 存在
            // 将之前 包装disguised 插入 TheBucket,  
            // TheBucket = { first : second } =  { void * : ObjectAssociation }
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            // 如果second存在即value存在
            if (refs_result.second) {
                /* it's the first association we make */
                // refs_result.second = true代表第一次  即首次关联, 
                isFirstAssociation = true;
            }

            /* establish or replace the association */
            // 建立或取代 关联 留意下 这个时候associations还没有插入值
            auto &refs = refs_result.first->second;
            // 第二次 try_emplace 插入
            // key 与 association关联  
            // 这时候的key就是成员变量的key,将 association 插入桶中。有值的情况下没有插入,没有值的情况下才插入。
            auto result = refs.try_emplace(key, std::move(association));
            if (!result.second) {
                // 第二次, 值会发生变化做新旧交换
                association.swap(result.first->second);
            }
        } else {
            // 如果 value 不存在
            // 值不存在或者空这种直接清除 refs.erase(it);
            auto refs_it = associations.find(disguised);
            if (refs_it != associations.end()) {
                // 找到对应的associations
                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);
                    }
                }
            }
        }
    }
    // 第一次时候标记对象是否有关联对象
    if (isFirstAssociation)
        object->setHasAssociatedObjects();

    // 释放
    association.releaseHeldValue();
}

关联对象设计图

关联对象是运行时创建用于存储值 value, 看下关联对象的设计图

关联对象

关联对象流程分析

AssociationsHashMap try_emplace 关系图

可看出内部实际是进行了查找,返回对应的 TheBucket。

关联对象实测流程

关联对象赋值
test_property 是我们设置的关联对象 关联对象打印
打印一下associationvalue, 对应我们传的值(传入参数) result
打印result, 初始second为true, first里面是其数据, 接下来走try_emplace进行插值 try_emplace
try_emplace 中由于第一次 BucketT 没有值, 所以走下面插入新值 读result

之后走第一次标记关联对象setHasAssociatedObjects以及释放旧value方法releaseHeldValue

释放对象 setHasAssociatedObjects

关联对象释放

void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object, /*deallocating*/false);
    }
}

内部调用_object_remove_assocations, 看一下

// Unlike setting/getting an associated reference,
// this function is performance sensitive because of
// raw isa objects (such as OS Objects) that can't track
// whether they have associated objects.

// 与设置/获取关联引用不同
// 此函数对性能敏感,因为原始isa对象(如OS对象)无法跟踪它们是否具有关联对象。
void
_object_remove_assocations(id object, bool deallocating)
{
    // 创建 空ObjectAssociationMap refs
    ObjectAssociationMap refs{};

    {
        // 创建 AssociationsManager
        AssociationsManager manager;
        // 创建 AssociationsHashMap
        AssociationsHashMap &associations(manager.get());
        // associations 中找到 iterator 迭代查询器
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        
        // 迭代查询器 不等于 associations最后一个
        if (i != associations.end()) {
            
            // 将对象对应的 ObjectAssociationMap 存入refs临时空间
            refs.swap(i->second);

            // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
            // 如果我们没有取消分配,那么将保留 SYSTEM_OBJECT 关联对象。
            bool didReInsert = false;
            // 对象非释放的情况下
            if (!deallocating) {
                for (auto &ref: refs) {
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
                        // 重新将系统的关联对象插入
                        i->second.insert(ref);
                        didReInsert = true;
                    }
                }
            }
            
            // 对象释放的情况下
            if (!didReInsert)
                // 清理迭代器i
                associations.erase(i);
        }
    }

    // Associations to be released after the normal ones.
    // 将在正常关联之后释放关联。
    SmallVector<ObjcAssociation *, 4> laterRefs;

    // release everything (outside of the lock).
    // 释放所有
    for (auto &i: refs) {
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // If we are not deallocating, then RELEASE_LATER associations don't get released.
            if (deallocating)
                laterRefs.append(&i.second);
        } else {
            // 释放非系统的关联对象
            i.second.releaseHeldValue();
        }
    }
    
    // 循环释放 laterRefs
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}

关联对象总结

设值流程

取值流程

上一篇 下一篇

猜你喜欢

热点阅读