类的加载(三)

2020-10-30  本文已影响0人  _涼城
类扩展
类扩展 VS 分类

category

extension

类扩展探索

main.m中实现Teacher类的扩展,通过Clang进行编译

查看Teacher类拓展的方法,在编译过程中,方法就直接添加到了 methodlist中,作为类的一部分,即编译时期直接添加到本类里面

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[8];
} _OBJC_$_INSTANCE_METHODS_Teacher __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    8,
    {{(struct objc_selector *)"ext_instanceMethod", "v16@0:8", (void *)_I_Teacher_ext_instanceMethod},
    {(struct objc_selector *)"ext_classMethod", "v16@0:8", (void *)_I_Teacher_ext_classMethod},
    {(struct objc_selector *)"instanceMethod", "v16@0:8", (void *)_I_Teacher_instanceMethod},
    {(struct objc_selector *)"classMethod", "v16@0:8", (void *)_I_Teacher_classMethod},
    {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_Teacher_ext_name},
    {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_Teacher_setExt_name_},
    {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_Teacher_ext_name},
    {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_Teacher_setExt_name_}}
};
关联对象

在分类中通过runtime关联对象

@interface Person (caterogy)
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
@end

@implementation LGPerson (LG)

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

@end
设值流程
objc_setAssociatedObject

调用SetAssocHook.get(),实际上是调用_base_objc_setAssociatedObject函数


//实际调用的方法
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};

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

_object_set_associative_reference
  1. 创建一个 AssociationsManager 管理类

  2. 获取唯一的全局静态哈希Map:AssociationsHashMap

  3. 判断是否插入的关联值value是否存在,不存在则走关联对象插入空流程

  4. 通过try_emplace方法,并创建一个空的 ObjectAssociationMap 去取查询的键值对:

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

  6. 通过setHasAssociatedObjects方法标记对象存在关联对象即置isa指针has_assoc属性为true

  7. 用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT 中的空

  8. 标记一下 ObjectAssociationMap第一次false

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));
    // 包装了一下 对象
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    // 包装一下 policy - value
    ObjcAssociation association{policy, value};

    // retain the new value (if any) outside the lock.(retain 引用计数+1,copy 发送消息)
    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);

                    }
                }
            }
        }
    }

    // release the old value (outside of the lock).
    association.releaseHeldValue();
}
关联对象插入空流程
  1. 根据DisguisedPtr 找到AssociationsHashMap中的iterator迭代查询器
  2. 清理迭代器
  3. 如果插入空值相当于删除
AssocaitionsHashMap
Buckets
上一篇 下一篇

猜你喜欢

热点阅读