什么是Category?
- Category是Objective-C 2.0之后添加的语言特性,分类、类别其实都是指的Category。
- Category的主要作用是为已经存在的类添加方法。也可以说是将庞大的类代码按逻辑划入几个分区。
- 分类的特性是可以在运行时阶段动态的为已有的类添加新行为。
- Objective-C 中Category 就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。
分类和扩展
- 扩展(Extension)有时候被称为匿名分类。但是两者实质上不是一个内容。
- 扩展是在编译阶段与该类同时编译的,是类的一部分。扩展中声明的方法只能在该类的
@implementation
中实现。所以这也就意味着我们无法对系统的类使用扩展。 - 同时与分类不同,扩展不但可以声明方法,还可以声明成员变量,这是分类所做不到的。
Category的实质
Category结构体
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; //实例属性
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties; //类属性
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
分类的结构体中可以为类添加对象方法、类方法、协议、属性,但是并没有成员变量
从C++开始看起
我们将分类的.m文件转成c++文件来了解一下
我们先搞个分类
@interface NSObject (testCategory) <TestProtocol>
@property (nonatomic, copy) NSString *personName;
- (void)test;
+ (void)secondTest;
@end
声明一个实例方法、一个类方法、一个属性 分类遵循一个协议 协议里也是一个类方法和对象方法
@protocol TestProtocol <NSObject>
- (void)protocolMethod;
+ (void)protocolClassMethod;
@end
clang -rewrite-objc NSObject+testCategory.m
转成C++
【category结构体】
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;
};
【category结构体赋值】
static struct _category_t _OBJC_$_CATEGORY_NSObject_$_testCategory __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSObject",
0, // &OBJC_CLASS_$_NSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_testCategory,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_NSObject_$_testCategory,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_NSObject_$_testCategory,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_NSObject_$_testCategory,
};
【结构体数组】
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_NSObject_$_testCategory,
};
- 我们可以看到重点的三个元素
-
- category结构体
-
- category结构体的赋值语句
-
- category结构体数组
对象方法列表结构体
【对象方法的实现】
static void _I_NSObject_testCategory_test(NSObject * self, SEL _cmd) {
printf("正在打印test方法");
}
static void _I_NSObject_testCategory_protocolMethod(NSObject * self, SEL _cmd) {
printf("正在打印协议对象方法");
}
【对象方法列表结构体】
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_testCategory __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"test", "v16@0:8", (void *)_I_NSObject_testCategory_test},
{(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_NSObject_testCategory_protocolMethod}}
};
- (void)test
和- (void)protocolMethod
方法的实现- 对象方法结构体列表结构体
只要是在Category中实现了的对象方法(包括代理中的对象方法)。都会添加到对象方法列表结构体_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_testCategory
中来,如果仅仅是定义,没有实现,不会加进来
类方法列表结构体
【类方法的实现】
static void _C_NSObject_testCategory_secondTest(Class self, SEL _cmd) {
printf("正在打印secondTest方法");
}
static void _C_NSObject_testCategory_protocolClassMethod(Class self, SEL _cmd) {
printf("正在打印类对象方法");
}
【类方法列表结构体】
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_CLASS_METHODS_NSObject_$_testCategory __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"secondTest", "v16@0:8", (void *)_C_NSObject_testCategory_secondTest},
{(struct objc_selector *)"protocolClassMethod", "v16@0:8", (void *)_C_NSObject_testCategory_protocolClassMethod}}
};
+ (void)secondTest
和+protocolClassMethod
类方法的实现- 类方法列表结构体
只要是Category中实现了的类方法(包括代理中的类方法)。都会添加到类方法列表结构体_OBJC_$_CATEGORY_CLASS_METHODS_NSObject_$_testCategory
中来
协议列表结构体
【协议结构体】
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;
};
【分类中添加的协议列表结构体】
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];//为什么是1?
} _OBJC_PROTOCOL_REFS_TestProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSObject
};
【协议列表 对象方法列表结构体】
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_TestProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
};
【协议列表 类方法列表结构体】
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_TestProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"protocolClassMethod", "v16@0:8", 0}}
};
【结构体赋值】
struct _protocol_t _OBJC_PROTOCOL_TestProtocol __attribute__ ((used)) = {
0,
"TestProtocol",
(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_TestProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_TestProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_TestProtocol,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_TestProtocol
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_TestProtocol = &_OBJC_PROTOCOL_TestProtocol;
- 协议列表结构体
- 协议列表 对象方法列表结构体
- 协议列表 类方法列表结构体
- 结构体赋值语句
属性列表结构体
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[1];
} _OBJC_$_PROP_LIST_NSObject_$_testCategory __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"personName","T@\"NSString\",C,N"}}
};
从【属性列表结构体】源码中我们可以看到:只有person分类中添加的属性列表结构体_OBJC_$_PROP_LIST_NSObject_$_testCategory
,没有成员变量结构体_ivar_list_t
结构体。更没有对应的set/get
方法相关的内容。
这也说明了Category中不能添加成员变量这一事实。
category总结
主要包含下面几部分内容
- _method_list_t 类型的【对象方法列表结构体】;
- _method_list_t 类型的【类方法列表结构体】;
- _protocol_list_t 类型的【协议列表结构体】;
- _prop_list_t 类型的【属性列表结构体】。
_category_t
结构体中并不包含_ivar_list_t
类型,也就是不包含【成员变量结构体】
分类在运行期做了什么
想搞明白这个问题,我们就要知道什么时候调用了分类的方法
_objc_init
这个函数是runtime的初始化函数,我们从_objc_init
开始入手
_objc_init
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
map_images读取资源(images代表资源模块),来到map_images_nolock
函数中找到_read_images
函数,在_read_images
函数中找到与分类相关的代码
_read_images
// Discover categories.
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
- 获取
category
列表list - 遍历
category list
中的每一个category
- 获取
category
的对应的主类cls,如果没有cls就跳过(continue)这个继续获取下一个 - 如果其有对应的主类,并其有实例方法、协议、属性,则调用
addUnattachedCategoryForClass
,同时如果cls中有实现的话,进一步调用remethodizeClass
方法 - 如果其有对应的主类,并其有类方法、协议,则调用
addUnattachedCategoryForClass
,同时如果cls的元类有实现的话,就进一步调用remethodizeClass
方法
4、5主要是分类对应的主类是元类对象还是类对象
看一下对应的addUnattachedCategoryForClass的方法
addUnattachedCategoryForClass
static void addUnattachedCategoryForClass(category_t *cat, Class cls,
header_info *catHeader)
{
runtimeLock.assertWriting();
// DO NOT use cat->cls! cls may be cat->cls->isa instead
NXMapTable *cats = unattachedCategories();
category_list *list;
list = (category_list *)NXMapGet(cats, cls);
if (!list) {
list = (category_list *)
calloc(sizeof(*list) + sizeof(list->list[0]), 1);
} else {
list = (category_list *)
realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
}
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
NXMapInsert(cats, cls, list);
}
static NXMapTable *unattachedCategories(void)
{
runtimeLock.assertWriting();
//全局对象
static NXMapTable *category_map = nil;
if (category_map) return category_map;
// fixme initial map size
category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);
return category_map;
}
- 通过
unattachedCategories()
函数生成一个全局对象cats
- 我们从这个单例对象中查找
cls
,获取一个category_list *list
列表 - 要是没有
list
指针。那么我们就生成一个category_list
空间 - 要是有
list
指针,我们就在该指针的基础上再分配处category_list
大小的空间 - 在这新分配好的空间,将这个
cat
和catHeader
写入 - 将数据插入到
cats
中,key----->cls value------>list
这段代码对于我们来说,对分类的实现部分关系并不大,其仅仅是把类和category做一个关联映射,而remethodizeClass
才是真正去处理添加事宜的功臣
remethodizeClass
static void remethodizeClass(Class cls)
{
//分类数组
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
还是没有得到我们需要的信息,其核心是调用了attachCategories
函数把我们的分类信息附加到该类中
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// 创建方法列表、属性列表、协议列表,用来存储分类的方法、属性、协议
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0; // 记录方法的数量
int propcount = 0; // 记录属性的数量
int protocount = 0; // 记录协议的数量
int i = cats->count; // 从分类数组最后开始遍历,保证先取的是最新的分类
bool fromBundle = NO; // 记录是否是从 bundle 中取的
while (i--) { // 从后往前依次遍历
auto& entry = cats->list[i]; // 取出当前分类
// 取出分类中的方法列表。如果是元类,取得的是类方法列表;否则取得的是对象方法列表
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist; // 将方法列表放入 mlists 方法列表数组中
fromBundle |= entry.hi->isBundle(); // 分类的头部信息中存储了是否是 bundle,将其记住
}
// 取出分类中的属性列表,如果是元类,取得的是 nil
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// 取出分类中遵循的协议列表
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
上面部分的代码仅仅是将分类中的方法、属性、协议插入到各自对应的大数组中
注意是从后往前加入的(为啥???)
--------------
// 取出当前类 cls 的 class_rw_t 数据
auto rw = cls->data();
// 存储方法、属性、协议数组到 rw 中【注意是rw哦】
// 准备方法列表 mlists 中的方法【为什么需要准备方法列表这一步?】
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// 将新方法列表添加到 rw 中的方法列表中
rw->methods.attachLists(mlists, mcount);
// 释放方法列表 mlists
free(mlists);
// 清除 cls 的缓存列表
if (flush_caches && mcount > 0) flushCaches(cls);
// 将新属性列表添加到 rw 中的属性列表中
rw->properties.attachLists(proplists, propcount);
// 释放属性列表
free(proplists);
// 将新协议列表添加到 rw 中的协议列表中
rw->protocols.attachLists(protolists, protocount);
// 释放协议列表
free(protolists);
}
- 先创建方法列表、属性列表、协议列表的新列表并且给它们分配内存,然后存储该cls所有的分类的方法、属性、协议,然后转交给了attachMethodLists方法(就是后面的那些写在一起了)
为什么需要准备方法列表这一步呢?
方法的查找算法是通过二分查找算法,说明sel-imp是有排序的,那么是如何排序的呢?
perpareMethodLists中主要调用了fixup方法 在 fixupMethodList 方法中会遍历 mlist,把 sel 中的名字跟地址设置到 meth,然后根据地址对 mlist 进行重新排序。
这也就意味着 remethodizeClass方法中实现类中方法(协议等)的序列化。
- attachLists方法保证其添加到列表的前面
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
- array()->lists: 类对象原来的方法列表,属性列表,协议列表,比如Person中的那些方法等
- addedLists:传入所有分类的方法列表,属性列表,协议列表,比如Person(Eat)、Person(Drink)中的那些方法等。
上面代码的作用就是通过memmove将原来的类找那个的方法、属性、协议列表分别进行后移,然后通过memcpy将传入的方法、属性、协议列表填充到开始的位置。
- 分类的方法、属性、协议只是添加到原有类上,并没有将原有类的方法、属性、协议进行完全替换
-
- 举个例子说明就是:假设原有类拥有 MethodA方法,分类也拥有 MethodA 方法,那> 么加载完分类之后,类的方法列表中会拥有两个 MethodA方法。
- 分类的方法、属性、协议会添加到原有类的方法列表、属性列表、协议列表的最前面,而原有类的方法列表、属性列表、协议列表则被移动到了列表的后面
-
- 因为运行时查找方法是顺着方法列表的顺序进行依次查找的,所以Category的方法会先被搜索到,然后直接指向,而原有类的方法则不被指向。这也是分类中的方法会覆盖掉原有类的方法最直接的原因。
分类的实现部分说起来很简单,不过源码确实有点恶心
load方法和initialize方法
load方法
我们知道,在类和category中都可以有+load方法,那么有两个问题:
1)、在类的+load方法调用的时候,我们可以调用category中声明的方法么?
2)、这么些个+load方法,调用顺序是咋样的呢?
并且在每个类中的load方法都添加了load方法
当在build Phases的Compile Sources中设置编译顺序如下时:
结果是
更换1和2的顺序
再当我们把主类放到这两个分类的编译顺序下面,是不是还会按照编译顺序运行?
可以看到并不是
带上子类呢?
对于load方法:
- 先调用类的load方法
-
- 按照编译先后顺序调用
-
- 调用子类的load之前会先调用父类的load
- 再调用分类的load
-
- 按照编译的先后顺序调用
对于“覆盖”的方法,会找到最后一个编译的方法,和我们上面理解的分类实现的方法一样
initialize
不带子类
initialize是在类的方法第一次被调用时执行
覆盖类中的方法 只执行一次
分类2的test在前面,先调用分类2,所以分类2的initialize方法先执行
带上子类
调用子类的分类的test方法,可以看到创建了两遍父类的initialize方法
为啥呢???
因为我在initialize方法中写了[super initialize],由于是系统自动调用,也不需要再调用 [super initialize] ,否则父类的initialize会被多次执行
假如写了[super initialize]这时,系统就会再回到父类,再打印一遍。所以会打印两遍
关联对象
虽然在分类中可以写@property添加属性,但是不会自动生成私有属性,也不会生成set,get方法的实现,只会生成set,get的声明,需要我们自己去实现。
具体怎么实现,就涉及到关联对象了
就像这样
我们为什么需要关联对象?
因为在分类中,@property并不会自动生成实例变量以及存取方法,所以一般使用关联对象为已经存在的类添加属性。
正如上面的图,我们需要objc_getAssociatedObject
和 objc_setAssociatedObject
两个方法帮我们实现在以及存在的类添加属性的问题
我们先来看这两个函数
基本使用
setAssociatedObject
objc_setAssociatedObject(self, personNameKey, personName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
参数一: id object
:给哪个对象添加属性,这里要给自己添加属性,使用self就行了
参数二:key
:设置关联对象的key,根据key获取关联对象的属性的值,在objc_getAssociatedObject
中通过次key获得属性的值并返回,相当于一个中间人
参数三:id value
:关联的值,也就是set方法传入的值给属性去保存
参数四:policy
:策略,属性以什么形式保存
有以下的几种:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, 指定一个弱引用相关联对象
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 指定相关的对象的强引用,非原子性
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, 指定相关的对象被复制,非原子性
OBJC_ASSOCIATION_RETAIN = 01401, 指定相关的对象的强引用,原子性
OBJC_ASSOCIATION_COPY = 01403 指定相关的对象被复制,原子性
};
getAssociatedObject
objc_getAssociatedObject(self, personNameKey);
两个参数
参数一:id object
:获取哪个对象里面的关联的属性
参数二:key
:什么属性,与objc_setAssociatedObject
中的key相对应,即通过key值去除value
移除所有关联对象
- (void)removeAssociatedObjects {
objc_removeAssociatedObjects(self);
}
可以看出关联对象的使用非常简单,接下来看看关联对象的底层原理
关联对象底层原理
核心技术有
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
直接开干
首先先分析一下这几个核心对象
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
// AssociationsManager中只有一个变量AssociationsHashMap
static AssociationsHashMap *_map;
public:
// 构造函数中加锁
AssociationsManager() { AssociationsManagerLock.lock(); }
// 析构函数中释放锁
~AssociationsManager() { AssociationsManagerLock.unlock(); }
// 构造函数、析构函数中加锁、释放锁的操作,保证了AssociationsManager是线程安全的
AssociationsHashMap &associations() {
// AssociationsHashMap 的实现可以理解成单例对象
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
其中只有一个变量AssociationsHashMap
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); }
};
我们发现AssociationsHashMap
继承自unordered_map
unordered_map
中是以key_type
和mapped_type
作为key和value
对应AssociationsHashMap
内源码
disguised_ptr_t
是key
ObjectAssociationMap *
是value
接着我们继续来看ObjectAssociationMap
// ObjectAssociationMap是字典,key是从外面传过来的key,例如@selector(hello),value是关联对象,也就是
// ObjectAssociation
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); }
};
ObjectAssociationMap
中同样以key、value
的方式存储着ObjcAssociation
继续看ObjcAssociation
class ObjcAssociation {
uintptr_t _policy;
// 值
id _value;
public:
// 构造函数
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
// 默认构造函数,参数分别为0和nil
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
我们发现ObjcAssociation
存储着policy
、value
,这两个值正是我们调用objc_setAssociatedobject
函数中传入的value和policy
这两个值最终是存储在ObjcAssociation
中的。
- 关联对象并不是存储在被关联对象本身内存中
- 关联对象存储在全局的统一的一个
AssociationsManager
中 - object作为key,一个被关联对象的所有关联对象都存储在同一个ObjectAssociationMap中
- object被关联的对象不能为nil
- 设置关联对象value为nil是,就相当于是移除关联对象
- 这个结构也十分巧妙
-
- 一个
objc
对象不光有一个属性需要关联时,假设要关联name
和age
这两个属性,我们就以objc
对象作为disguised_ptr_t
,然后value
是objectAssociationMap
这个字典类型,在这个字典类型中,分别使用@"name"
和@"age"
作为key,传递进行的值和策略生成objectAssociation
作为value
- 一个
-
- 如果有多个对象进行关联时,我们只需要在
AssociationsHashMap
中创造更多的键值对就可以解决这个问题
- 如果有多个对象进行关联时,我们只需要在
-
- 关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的一个
AssociationsManager
中
- 关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的一个
以及看完了这些核心函数,回到objc_setAssociatedObject
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
其内部调用了_object_set_associative_reference
这个方法
// 该方法完成了设置关联对象的操作
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
// 初始化空的ObjcAssociation(关联对象)
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
// 初始化一个manager
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 获取对象的DISGUISE值,作为AssociationsHashMap的key
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// value有值,不为nil
// break any existing association.
// AssociationsHashMap::iterator 类型的迭代器
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
// 获取到ObjectAssociationMap(key是外部传来的key,value是关联对象类ObjcAssociation)
ObjectAssociationMap *refs = i->second;
// ObjectAssociationMap::iterator 类型的迭代器
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 原来该key对应的有关联对象
// 将原关联对象的值存起来,并且赋新值
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
// 无该key对应的关联对象,直接赋值即可
// ObjcAssociation(policy, new_value)提供了这样的构造函数
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
// 执行到这里,说明该对象是第一次添加关联对象
// 初始化ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
// 赋值
(*refs)[key] = ObjcAssociation(policy, new_value);
// 设置该对象的有关联对象,调用的是setHasAssociatedObjects()方法
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// value无值,也就是释放一个key对应的关联对象
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
// 调用erase()方法删除对应的关联对象
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
// 释放旧的关联对象
if (old_association.hasValue()) ReleaseValue()(old_association);
}
首先根据我们传入的value
经过acquireValue
函数处理获取new_value
。acquireValue
函数内部其实时通过对策略的判断返回不同的值
// 根据policy的值,对value进行retain或者copy
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
之后创建AssociationsManager manager
,以及拿到Manager
内部的AssociationsHashMap
,对应代码就是associations
AssociationsHashMap &associations() {
// AssociationsHashMap 的实现可以理解成单例对象
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
之后我们看到传入的第一个参数object
object
经过DISGUISE
函数被转换成了disguised_ptr_t
类型的disguised_object
typedef uintptr_t disguised_ptr_t;
inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
DISGUISE
函数其实仅仅对object
做了位运算(取反?)
接着来看一下objc_getAssociatedObject
函数
// 获取关联对象的方法
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
调用了_object_get_associative_reference
这个方法
// 获取关联对象
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
// 获取到manager中的AssociationsHashMap
AssociationsHashMap &associations(manager.associations());
// 获取对象的DISGUISE值
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 获取ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 获取到关联对象ObjcAssociation
ObjcAssociation &entry = j->second;
// 获取到value
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
// 返回关联对像的值
return value;
}
和set是一样的道理,获取到manager中的AssociatuinsHashMap,然后通过DISGUISE(object)
获取到obj对应到AssociationsHashMap
的key值。通过这个key值来通过迭代器去查找是否存在于AssociationsHashMap
中。后面就是判空和取值的过程。
说完了set和get,下面就是remove的过程
// 移除对象object的所有关联对象
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
看一下_object_remove_assocations
这个函数
// 移除对象object的所有关联对象
void _object_remove_assocations(id object) {
// 声明了一个vector
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
// 如果map size为空,直接返回
if (associations.size() == 0) return;
// 获取对象的DISGUISE值
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
通过循环,将object对象向对应的所有关联对象全部删除。