类的加载(三)
2020-10-30 本文已影响0人
_涼城
类扩展
类扩展 VS 分类
category
- 专门用来给类添加新的方法
- 不能给类添加成员属性,添加了成员变量,也无法取到
- 分类中用
property定义变量,只会生成变量的getter、setter方法的声明,不能生成方法实现和带下划线的成员变量 。
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
-
创建一个
AssociationsManager管理类 -
获取
唯一的全局静态哈希Map:AssociationsHashMap -
判断是否插入的
关联值value是否存在,不存在则走关联对象插入空流程 -
通过
try_emplace方法,并创建一个空的ObjectAssociationMap去取查询的键值对: -
如果发现
没有这个key就插入一个 空的 BucketT进去并返回true -
通过
setHasAssociatedObjects方法标记对象存在关联对象即置isa指针的has_assoc属性为true -
用当前
policy 和 value组成了一个ObjcAssociation替换原来BucketT 中的空 -
标记一下
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();
}
关联对象插入空流程
- 根据DisguisedPtr 找到AssociationsHashMap中的iterator迭代查询器
- 清理迭代器
- 如果插入空值相当于删除
AssocaitionsHashMap
BucketsNumEntriesNumTombstonesNumBuckets
Buckets
-
Bucket-
DisguisedPtr---ObjectAssociationMap
-