iOS底层学习:类的扩展和关联对象

类的扩展和分类

  1. category:分类、类别
  • 给类增加方法
  • 不能添加成员变量
  • 可以使用runtime给分类添加属性
  • 分类中添加的属性指挥生成对应的settergetter方法的声明,不能生成方法实现和带下划线的成员变量。
  1. extension:类扩展
  • 可以添加成员属性,属于私有。
  • 可以添加方法,属于私有。

关于类的扩展,是放在类的声明之后类的实现之前。类的扩展里面放的多是私有方法和成员变量。类的扩展会在编译期就作为类的一部分写入到类信息中。另外,类的扩展只是一个声明,它依赖主类需要在主类内部完成实现。

关联对象

分类不能添加属性,它只是声明了settergetter方法,没有实现,那么就需要借助关联对象来实现这个功能。

分类.h中
@property (nonatomic, copy) NSString *name;

分类.m中
- (void)setCate_name:(NSString *)name{
    /**
     (对象,标识符,value, 策略)
     */
    objc_setAssociatedObject(self, "name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

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

关联对象源码探索

关联对象如何运作的

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

static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};

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

关联对象设值底层调用的方法就是_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)

_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    ...
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    ObjcAssociation association{policy, value};

    association.acquireValue();
    //工作代码块
    {代码块}
    association.releaseHeldValue();
}

传递进来的object被包装成了DisguisedPtr,对应的valuepolicy打包成了ObjcAssociation
然后具体的操作放在了代码块中

    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);

                    }
                }
            }
        }
    }
  • acquireValue() 更具传递进来的polic对值进行处理
inline void acquireValue() {
        if (_value) {
            switch (_policy & 0xFF) {
            case OBJC_ASSOCIATION_SETTER_RETAIN:
                _value = objc_retain(_value);
                break;
            case OBJC_ASSOCIATION_SETTER_COPY:
                _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                break;
            }
        }
    }

基本流程:
1:创建一个 AssociationsManager 管理类。
2:拿到一张全局唯一的:AssociationsHashMap
3:判断是否插入的关联值value是否存在,存在走第4步,不存在则会移除关联对象。
4:try_emplace,并创建一个空的 ObjectAssociationMap 去取查询的键值对:
5:如果发现没有这个 key 就插入一个 空的 BucketT进去并返回true
6:通过setHasAssociatedObjects方法标记对象存在关联对象即置isa指针的has_assoc属性为true
7:用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT 中的空
8:标记一下 ObjectAssociationMap 的第一次为 false

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容