分类和类扩展
在OC类的加载中我们分析过分类的底层实现,其实是一个category_t的结构体,那现在来看一下类扩展的底层实现。
//定义一个类以及其扩展
@interface Teacher : NSObject
@property (nonatomic, strong) NSString *normalName;
- (void)normalSayHello;
@end
@interface Teacher ()
@property (nonatomic,copy) NSString *extName;
- (void)ext_sayhello;
@end
@implementation Teacher
- (void)normalSayHello {
}
- (void)ext_sayhello {
}
@end
//然后用clang编译器转换成c++代码看一下底层实现逻辑, clang -rewrite-objc main.m -o main1.cpp
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_Teacher __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
2,
{{(unsigned long int *)&OBJC_IVAR_$_Teacher$_normalName, "_normalName", "@\"NSString\"", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_Teacher$_extName, "_extName", "@\"NSString\"", 3, 8}}
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[6];
} _OBJC_$_INSTANCE_METHODS_Teacher __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
6,
{{(struct objc_selector *)"normalSayHello", "v16@0:8", (void *)_I_Teacher_normalSayHello},
{(struct objc_selector *)"extSayhello", "v16@0:8", (void *)_I_Teacher_extSayhello},
{(struct objc_selector *)"normalName", "@16@0:8", (void *)_I_Teacher_normalName},
{(struct objc_selector *)"setNormalName:", "v24@0:8@16", (void *)_I_Teacher_setNormalName_},
{(struct objc_selector *)"extName", "@16@0:8", (void *)_I_Teacher_extName},
{(struct objc_selector *)"setExtName:", "v24@0:8@16", (void *)_I_Teacher_setExtName_}
};
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Teacher __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"normalName","T@\"NSString\",&,N,V_normalName"}}
};
可以看到extSayhello和普通的normalSayHello一起在类的methodlist中,extName和normalName都在实例变量ivarlist中,但extName不在proplist中,所以extName不能对外暴露,另外extName也生成了对应的get、set方法。
再来看一下分类在底层的实现:
//定义这样一个分类,然后还是clang转换成c++代码查看
@interface Teacher (CategoryA)
@property (nonatomic, copy) NSString *catName;
- (void)categorySayhello;
@end
@implementation Teacher(CategoryA)
- (void)categorySayhello {}
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Teacher_$_CategoryA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"categorySayhello", "v16@0:8", (void *)_I_Teacher_CategoryA_categorySayhello}}
};
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Teacher_$_CategoryA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"catName","T@\"NSString\",C,N"}}
};
static struct _category_t _OBJC_$_CATEGORY_Teacher_$_CategoryA __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Teacher",
0, // &OBJC_CLASS_$_Teacher,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Teacher_$_CategoryA,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Teacher_$_CategoryA,
};
可以看到分类的信息统一是生成了一个category_t的对象,而extName虽然也在proplist中,但没有对应的get、set方法。分类的方法categorySayhello也是重新定义了一个结构体存储,和主类的方法分开的。
关联对象的代码分析
那在分类中的属性我们通常实现其get、set方法都会使用到runtime的api:objc_setAssociatedObject,先看一下它的代码实现:
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
//这个SetAssocHook是个静态变量,调用get方法就是拿初始化变量传进去的函数指针,所以SetAssocHook.get()就相当于_base_objc_setAssociatedObject
SetAssocHook.get()(object, key, value, policy);
}
static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_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);
}
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
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类型的
DisguisedPtr<objc_object> disguised{(objc_object *)object};
//把传进来的policy和value包装成ObjcAssociation类型的
ObjcAssociation association{policy, value};
//根据policy对传进来的value进行retain或者调用copy方法
association.acquireValue();
{
//创建一个AssociationsManager对象
AssociationsManager manager;
//拿到全局的关联对象表AssociationsHashMap
AssociationsHashMap &associations(manager.get());
if (value) {
//尝试从全局关联对象表中查找该对象的信息,disguised就是object这个对象的包装,
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
//second表示取值时是否取到,如果未取到,会先插入一个默认值并返回出来,但在second中有标识是否真的找到,为true表示未找到,false表示找到了
if (refs_result.second) {
//如果没拿到,则object是第一次放到关联对象中来,需要把对象标记一下,方便在对象释放时移除全局关联对象表中的数据
object->setHasAssociatedObjects();
}
//一个对象可能有多个关联属性,所以取出来所有的关联属性信息
auto &refs = refs_result.first->second;
//尝试根据本次传进来的key会所有的关联属性中去找,如果未找到会先插入一个默认值然后返回出来
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
//如果找到了,用association中的value和policy替换旧的值
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);
}
}
}
}
}
//如果前面retain过了,则此时release掉
association.releaseHeldValue();
}
关联对象的数据结构
通过分析我们可以发现AssociationsManager并不是全局唯一的,它内部使用的AssociationsHashMap才是全局的关联对象表,另外AssociationsHashMap中有会有多个对象信息ObjectAssociationMap,而一个对象可以有多个关联属性所以ObjectAssociationMap中又有多个ObjcAssociation信息。这样就构成了一张二维的hash表结构。
关联对象数据结构
网友评论