Category 简介
-
Category 是
Objective-C 2.0
之后添加的语言特性,其主要作用是为已经存在的类添加方法(无论工程中是否有该类的源码)。Category 可以做到在既不子类化,也不侵入一个类的源码的情况下,为原有的类添加新的方法,从而实现扩展一个类或者分离一个类的目的 -
程序在启动时,RunTime 会把 Category 中的内容附加到宿主类中
-
本文主要用于介绍 Category 的底层原理,关于 Category 的使用方式与注意事项,请参考:
Objective-C 的 Category 与 Extension
Category 的本质
-
Category 结构体简介
Category(分类)被定义为指向
struct category_t
的指针,其数据结构如下:// 一个不透明的类型,用于表示 Objective-C 中的一个分类 typedef struct category_t *Category; // 表示一个类的一个分类 struct category_t { const char *name; // 宿主类名称 classref_t cls; // 宿主类对象,在运行时阶段通过(宿主类名称)找到对应的(宿主类对象) struct method_list_t *instanceMethods; // 分类的对象方法列表 struct method_list_t *classMethods; // 分类的类方法列表 struct protocol_list_t *protocols; // 分类所遵守的协议列表(注意:不是分类所定义的协议列表) struct property_list_t *instanceProperties; // 分类的属性列表 };
从
struct category_t
的定义中可以看出
Category 可以为类添加:对象方法、类方法、协议、属性
Category 不能为类添加:成员变量 -
编译 Category 的 C++ 源码
想要了解 Category 的本质,需要借助 Category 的 C++ 源码
① 新建一个继承自
NSObject
的Person
类
② 新建一个UsingToolProtocol
协议
③ 为Person
类新建一个 Category:Addition
,在其中添加:协议、属性、对象方法、类方法
④ 将Person+Addition.m
编译为Person+Addition.cpp
~/Desktop/Training/CategoryDemo/CategoryDemo > xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Addition.m -o Person+Addition.cpp
⑤ 当我们得到
Person+Addition.cpp
文件之后,就会神奇地发现:这是一个 1.7M 大小,拥有近 3.5W 行代码的庞大文件。不过不用慌,与 Category 相关的 C++ 源码在文件的最底部。删除其他无关代码,只保留 Category 有关的代码,大概就会剩下 200 多行代码。下面我们根据 Category 结构体的不同成员,分模块来讲解一下 -
C++ 源码中『Category 结构体』
// C++ 源码中关于分类结构体的定义 与 RunTime 源码中关于分类结构体的定义,在本质上是一一对应的,可以看做是同一个结构体 struct _category_t { const char *name; struct _class_t *cls; const struct _method_list_t *instance_methods; const struct _method_list_t *class_methods; const struct _protocol_list_t *protocols; const struct _prop_list_t *properties; }; // 为 Person 类的 Addition 分类结构体赋值 static struct _category_t _OBJC_$_CATEGORY_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = { "Person", 0, // &OBJC_CLASS_$_Person, (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition, (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition, (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Addition, (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Addition, }; // Person 类的分类结构体数组,存放着与 Person 类相关的分类,如果 Person 类有多个分类,则分类结构体数组中存放对应数目的分类结构体 static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= { &_OBJC_$_CATEGORY_Person_$_Addition, };
-
C++ 源码中 Category 的『对象方法列表结构体』
// Addition 分类中定义的对象方法 -instanceMethodEat 的实现 static void _I_Person_Addition_instanceMethodEat(Person * self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_0); } // Addition 分类中定义的对象方法 -instanceMethodSleep 的实现 static void _I_Person_Addition_instanceMethodSleep(Person * self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_1); } // UsingToolProtocol 协议中定义的对象方法 -usingCompputer 的实现 static NSString * _Nonnull _I_Person_Addition_usingCompputer(Person * self, SEL _cmd) { return (NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_4; } // NSCopying 协议中定义的对象方法 -copyWithZone: 的实现 static id _Nonnull _I_Person_Addition_copyWithZone_(Person * self, SEL _cmd, NSZone * _Nullable zone) { return self; } // 方法列表结构体的定义 + 分类对象方法列表结构体的赋值 // 只要是 Addition 分类中实现了的对象方法(包括所遵守的协议中的对象方法),都会添加到对象方法列表结构体 _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition 中来。如果只是在 Addition 分类中定义,而没有实现,则不会添加 static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[4]; } _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 4, {{(struct objc_selector *)"instanceMethodEat", "v16@0:8", (void *)_I_Person_Addition_instanceMethodEat}, {(struct objc_selector *)"instanceMethodSleep", "v16@0:8", (void *)_I_Person_Addition_instanceMethodSleep}, {(struct objc_selector *)"usingCompputer", "@16@0:8", (void *)_I_Person_Addition_usingCompputer}, {(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", (void *)_I_Person_Addition_copyWithZone_}} };
-
C++ 源码中 Category 的『类方法列表结构体』
// Addition 分类中定义的类方法 +classMethodRun 的实现 static void _C_Person_Addition_classMethodRun(Class self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_2); } // Addition 分类中定义的类方法 +classMethodWalk 的实现 static void _C_Person_Addition_classMethodWalk(Class self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_3); } // UsingToolProtocol 协议中定义的类方法 +usingBike 的实现 static NSString * _Nonnull _C_Person_Addition_usingBike(Class self, SEL _cmd) { return (NSString *)&__NSConstantStringImpl__var_folders_92_vshp0dxd0cg90_kzw6k3mlfm0000gn_T_Person_Addition_d8df92_mi_5; } // UsingToolProtocol 协议中定义的类方法 +allocWithZone: 的实现 static instancetype _C_Person_Addition_allocWithZone_(Class self, SEL _cmd, struct _NSZone *zone) { return ((id (*)(id, SEL))(void *)objc_msgSend)((id)((id (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("alloc")), sel_registerName("init")); } // 方法列表结构体的定义 + 分类类方法列表结构体的赋值 // 只要是 Addition 分类中实现了的类方法(包括所遵守的协议中的类方法),都会添加到类方法列表结构体 _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition 中来。如果只是在 Addition 分类中定义,而没有实现,则不会添加 static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[4]; } _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 4, {{(struct objc_selector *)"classMethodRun", "v16@0:8", (void *)_C_Person_Addition_classMethodRun}, {(struct objc_selector *)"classMethodWalk", "v16@0:8", (void *)_C_Person_Addition_classMethodWalk}, {(struct objc_selector *)"usingBike", "@16@0:8", (void *)_C_Person_Addition_usingBike}, {(struct objc_selector *)"allocWithZone:", "@24@0:8^{_NSZone=}16", (void *)_C_Person_Addition_allocWithZone_}} };
-
C++ 源码中 Category 的『协议列表结构体』
// 方法列表结构体的定义 + UsingToolProtocol 对象方法列表结构体的赋值 static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1]; } _OBJC_PROTOCOL_INSTANCE_METHODS_UsingToolProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"usingCompputer", "@16@0:8", 0}} }; // 方法列表结构体的定义 + UsingToolProtocol 类方法列表结构体的赋值 static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1]; } _OBJC_PROTOCOL_CLASS_METHODS_UsingToolProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"usingBike", "@16@0:8", 0}} };
// C++ 源码中关于协议结构体的定义 与 RunTime 源码中关于协议结构体的定义,在本质上是一一对应的,可以看做是同一个结构体 struct _protocol_t { void * isa; // NULL const char *protocol_name; const struct _protocol_list_t * protocol_list; // super protocols const struct method_list_t *instance_methods; const struct method_list_t *class_methods; const struct method_list_t *optionalInstanceMethods; const struct method_list_t *optionalClassMethods; const struct _prop_list_t * properties; const unsigned int size; // sizeof(struct _protocol_t) const unsigned int flags; // = 0 const char ** extendedMethodTypes; }; // UsingToolProtocol 协议结构体的赋值 struct _protocol_t _OBJC_PROTOCOL_UsingToolProtocol __attribute__ ((used)) = { 0, "UsingToolProtocol", (const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_UsingToolProtocol, (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_UsingToolProtocol, (const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_UsingToolProtocol, 0, 0, 0, sizeof(_protocol_t), 0, (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_UsingToolProtocol }; struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_UsingToolProtocol = &_OBJC_PROTOCOL_UsingToolProtocol; // NSCopying 协议结构体的赋值 struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = { 0, "NSCopying", 0, (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying, 0, 0, 0, 0, sizeof(_protocol_t), 0, (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying }; struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_NSCopying = &_OBJC_PROTOCOL_NSCopying; // 协议列表结构体的定义 + 分类协议列表结构体的赋值 // 只要是 Addition 分类中所遵守的协议(注意:不是 Addition 分类中所定义的协议),都会添加到协议列表结构体 _OBJC_CATEGORY_PROTOCOLS_$_Person_$_Addition 中来。如果只是在 Addition 分类中定义,而没有遵守,则不会添加 static struct /*_protocol_list_t*/ { long protocol_count; // Note, this is 32/64 bit struct _protocol_t *super_protocols[2]; } _OBJC_CATEGORY_PROTOCOLS_$_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = { 2, &_OBJC_PROTOCOL_UsingToolProtocol, &_OBJC_PROTOCOL_NSCopying };
-
C++ 源码中 Category 的『属性列表结构体』
// C++ 源码中关于属性结构体的定义 与 RunTime 源码中关于属性结构体的定义,在本质上是一一对应的,可以看做是同一个结构体 struct _prop_t { const char *name; const char *attributes; }; // 属性列表结构体的定义 + 分类属性列表结构体的赋值 static struct /*_prop_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count_of_properties; struct _prop_t prop_list[2]; } _OBJC_$_PROP_LIST_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_prop_t), 2, {{"name","T@\"NSString\",&,D,N"}, {"age","Tq,D,N"}} };
-
C++ 源码中 Category 的『其他细节』
① 因为
PersonProtocol
协议在Person+Addition
分类中只是被定义,没有被遵守,所以PersonProtocol
协议在编译后的 C++ 源码中,变成了一块注释。这也从侧面说明了:分类中的协议列表,存储的是分类所遵守的协议,而不是分类所定义的协议// @protocol PersonProtocol <NSObject> // - (void)personProtocolInstanceMethod00; // - (void)personProtocolInstanceMethod01; // + (void)personProtocolClassMethod00; // + (void)personProtocolClassMethod01; /* @end */
② 在编译后的 C++ 源码中,没有找到分类关于成员变量结构体的定义,也没有找到分类关于成员变量列表结构体的定义,更没有找到分类关于属性
getter
、setter
的实现。这也从侧面说明了:分类中不能添加成员变量;分类中添加的属性,只会生成getter
、setter
的声明,不会生成getter
、setter
的实现
Category 的加载流程
-
(dyld 加载和链接动态库的流程)与(runtime 库的加载)
在 MachO && dyld(五)中,我们介绍了 dyld 加载和链接动态库的流程,整个流程可细分为 9 步 :
① 设置运行环境
② 加载系统共享缓存
③ 实例化主程序
④ 加载插入的动态库
⑤ 链接主程序
⑥ 链接插入的动态库
⑦ 执行弱符号绑定
⑧ 执行初始化方法
⑨ 查找 App 入口点并返回这里重点关注:⑧ 执行初始化方法,在这一步中:
dyld
的ImageLoaderMachO::doModInitFunctions
函数会去初始化libSystem.dylib
库,并调用libSystem.dylib
库的libSystem_initializer
函数而
libSystem_initializer
函数会去初始化libdispatch.dylib
库,并调用libdispatch.dylib
库的libdispatch_init
函数。接着libdispatch_init
函数又会去调用_os_object_init
函数最后
libdispatch.dylib
库的_os_object_init
函数会去初始化libobjc.dylib
库,并调用libobjc.dylib
库的_objc_init
函数整个函数调用栈如下图所示:
其中,_objc_init
为 RunTime 的入口函数,用于进行一些初始化操作。其源码如下:// path: objc4-756.2/runtime/objc-os.mm // Runtime 的入口函数, 用于进行引导程序的初始化, 在库初始化之前由 libSystem 调用 void _objc_init(void) { static bool initialized = false; if (initialized) return; initialized = true; // 修复程序会延迟初始化直到找到一个 objc-using 的镜像为止 ? environ_init(); // 初始化运行时的环境变量. 主要是读取影响 Runtim 的一些环境变量. 如果需要, 也可以输出环境变量的帮助提示 (在终端上直接输入命令 exprot OBJC_HELP=1) tls_init(); // 初始化 TLS (安全传输层协议). 这里执行的是关于线程 key 的绑定, 比如每条线程 数据的析构函数 static_init(); // 初始化 C++ 的静态构造函数. 因为在 dyld 调用静态构造函数之前, libc 会调用 _objc_init(), 所以我们必须自己执行 C++ 静态构造函数. 这里只会初始化系统内置的 C++ 构造函数 lock_init(); // 初始化锁. objc 的锁完全是采用 C++ 的锁的那一套逻辑 exception_init(); // 初始化 libobjc 的异常处理系统 // 向 dyld 注册镜像相关的回调通知(map_images:映射镜像, load_images:加载镜像/初始化镜像, unmap_image:卸载镜像) _dyld_objc_notify_register(&map_images, load_images, unmap_image); }
可以看到,在 RunTime 执行完所有初始化操作之后,调用了
_dyld_objc_notify_register
函数。_dyld_objc_notify_register
是 dyld 向 RunTime 提供的接口函数,RunTime 通过此函数向 dyld 注册回调。当 dyld(映射镜像、初始化镜像、卸载镜像)时,会通过 RunTime 注册的这些回调通知 RunTime 进行相应的处理 -
探索 RunTime 加载与处理 Category 的入口点
在 dyld 映射镜像时,会调用 RunTime 的
map_images
函数,其函数调用栈如下图所示:
①map_images
函数用于映射镜像前的加锁操作,其源码如下所示:
②map_images_nolock
函数用于镜像信息的提取与统计,其源码如下所示:
③_read_images
函数用于读取镜像,进行(类、方法编号、协议、分类)等的处理,并输出统计信息。RunTime 加载与处理 Category 的流程由本函数的第 7 步开始,其源码如下所示:
可以看到,RunTime 加载与处理 Category 主要用到了 2 个函数:addUnattachedCategoryForClass
函数,用于提取宿主类未附加的分类的列表,建立未附加的分类与宿主类的映射关系remethodizeClass
函数,用于重建宿主类的(方法列表、属性列表、协议列表)
通过这 2 个函数达到了 2 个目的:
- 把 Category(分类) 的对象方法、协议、属性附加到宿主类上
- 把 Category(分类) 的类方法、协议附加到宿主类的 meta-class 上
-
RunTime 加载与处理 Category 的流程
① 调用
addUnattachedCategoryForClass
函数,从全局未附加的分类的列表中(cats
),获取宿主类未附加的分类的列表(list
),并建立未附加的分类(cat
)与宿主类的映射关系:
② 调用remethodizeClass
函数,重建宿主类的(方法列表数组、属性列表数组、协议列表数组):
可以看到,remethodizeClass
函数其实就做了一件事:调用attachCategories
函数,attachCategories
函数是 RunTime 用于加载与处理 Category 的核心函数:
attachCategories
函数中,用于整合宿主类(方法列表数组、属性列表数组、协议列表数组)与 Category(方法列表数组、属性列表数组、协议列表数组)的函数为attachLists
:
注意:addUnattachedCategoryForClass
函数主要是向一个全局哈希表category_map
里面添加宿主类对应的未附加的分类,一个宿主类所有的未附加的分类都在其list
中
unattachedCategoriesForClass
函数主要是从全局哈希表category_map
里面取出一个宿主类所有的未附加的分类列表list
Association Object(关联对象)底层原理
-
Association Object(关联对象)的简单使用
在 Category 中使用
@property
添加属性时,编译器只会生成属性getter
setter
方法的声明,并不会生成_成员变量
,更不会生成属性getter
setter
方法的实现(这将导致我们无法访问成员变量,也无法自己手动实现成员变量的存取方法)。那么在 Category 中添加一个不能访问的属性有什么意义呢?实际上,可以使用 RunTime 的 Association Object(关联对象)为 Category 已有的属性设置一个关联值,并添加
getter
setter
方法的实现// Person+SportLover.h #import "Person.h" @interface Person (SportLover) @property (nonatomic, strong) NSString* sportName; @end ------------------------------------------------------------------------------------------------ // Person+SportLover.m #import "Person+SportLover.h" #import <objc/runtime.h> // 关联对象中属性的 key 值,需要确保全局唯一性 static NSString* kSportNameKey = @"Person+SportLover.sportName"; @implementation Person (SportLover) // 通过 RunTime 的关联对象,为分类的属性实现 getter -(NSString *)sportName { return objc_getAssociatedObject(self, &kSportNameKey); } // 通过 RunTime 的关联对象,为分类的属性实现 setter -(void)setSportName:(NSString *)sportName { objc_setAssociatedObject(self, &kSportNameKey, sportName, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end
注意:
虽然可以通过 Association Object(关联对象)为 Category 的属性动态地添加getter
setter
方法的实现
但是编译器自始至终都没有为 Category 的属性生成带下划线的成员变量
所以在代码中,如果使用_成员变量
的方式调用 Category 属性对应的成员变量,程序还是会报错RunTime 中用于 添加、获取、移除 指定对象上的关联对象的函数,如下所示:
// 用给定的 key 与关联策略为指定的对象设置关联值 // param0.object 源对象 // param1.key 用来标记关联值的 key // param2.value 关联值 // param3.policy 关联策略 void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy) // 用给定的 key 获取指定对象的关联值 // param0.object 源对象 // param1.key 用来标记关联值的 key // return 关联值 id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key) // 移除指定对象上的所有关联值 // param0.object 源对象 void objc_removeAssociatedObjects(id _Nonnull object) // 关联策略 typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, //关联对象的属性是弱引用 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //关联对象的属性是强引用并且关联对象不使用原子性 OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //关联对象的属性是 copy 并且关联对象不使用原子性 OBJC_ASSOCIATION_RETAIN = 01401, //关联对象的属性是强引用并且关联对象使用原子性 OBJC_ASSOCIATION_COPY = 01403 //关联对象的属性是 copy 并且关联对象使用原子性 }; // 注意: objc_removeAssociatedObjects(...) 函数的作用是移除指定对象上的所有关联值 如果要移除指定对象上的某个关联值,可以使用 objc_setAssociatedObject(object, &key, nil, policy),即关联值 value 传 nil 关联对象由 AssociationsManager 统一管理 所有对象的关联内容统一放在一个容器:全局哈希表 AssociationsHashMap 中
Association Object(关联对象)
key
的常见用法,如下所示:// 1.使用静态常量指针 static const void * kPropertykey = &kPropertykey; objc_setAssociatedObject(object, kPropertykey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getAssociatedObject(object, kPropertykey); // 2.使用静态常量的地址(以下五种定义静态常量的方式均可使用) //static const char kPropertyKey; //static const char * kPropertyKey; //static const char * kPropertyKey = "ClassName+CategoryName.propertyName"; //static const NSString * kPropertyKey; static const NSString * kPropertyKey = @"ClassName+CategoryName.propertyName"; objc_setAssociatedObject(object, &kPropertykey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getAssociatedObject(object, &kPropertykey); // 3.使用 getter 方法的 SEL 作为 key(可读性高,有智能提示) objc_setAssociatedObject(object, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getAssociatedObject(object, @selector(getter));
-
Association Object(关联对象)的底层数据结构
① AssociationsManager
// 一个宿主对象的关联对象,并非存储在该宿主对象本身的内存中。而是所有宿主对象的关联对象统一存储在一个由 AssociationsManager 管理和维护的全局的容器中 // AssociationsManager 管理和维护了一个全局静态的 AssociationsHashMap,用于存储所有宿主对象的关联对象,并且使用锁机制保证了线程安全 class AssociationsManager { // associative references: object pointer -> PtrPtrHashMap. static AssociationsHashMap *_map; public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); } AssociationsHashMap &associations() { if (_map == NULL) _map = new AssociationsHashMap(); return *_map; } };
② AssociationsHashMap
// 哈希表,用于存储 disguised_ptr_t 和 ObjectAssociationMap 键值对 // disguised_ptr_t 是根据宿主对象 object 生成的,但不存在引用关系: // disguised_ptr_t disguised_object = DISGUISE(object); class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> { public: void *operator new(size_t n) { return ::malloc(n); } void operator delete(void *ptr) { ::free(ptr); } };
③ ObjectAssociationMap
// 哈希表,用于存储 key 和 ObjcAssociation 键值对 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> { public: void *operator new(size_t n) { return ::malloc(n); } void operator delete(void *ptr) { ::free(ptr); } };
④ ObjcAssociation
// 存储着关联策略 policy 和关联值 value class ObjcAssociation { uintptr_t _policy; id _value; public: ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {} ObjcAssociation() : _policy(0), _value(nil) {} uintptr_t policy() const { return _policy; } id value() const { return _value; } bool hasValue() { return _value != nil; } };
-
AssociationsManager、AssociationsHashMap、ObjectAssociationMap、ObjcAssociation 之间的关系
-
objc_setAssociatedObject 函数的底层实现
-
objc_getAssociatedObject 函数的底层实现
-
objc_removeAssociatedObjects 函数的底层实现
-
acquireValue 函数