例如,我们有一个类中有一个私有方法A,外界当然是调用不到的,但是我们可以通过创建该类新的分类,其中也定义一个方法A,只声明不实现,那么此时我们调用这个分类中的方法A就会调用该类中的私有方法A了,这就将私有方法公开化了。
我们知道,所有的OC类和对象,在runtime层都是用struct结构体进行封装的,category也是如此,它被定义为category_t
结构体,其数据结构如下:
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);
};
从它的结构中就能知道它不能添加实例变量,因为其中没有实例变量的定义。
我们先创建一个新的分类,其中定义一些方法和属性,方便我们解析。
新建一个继承自NSObject的类的分类:
@interface TestNSObject (TestCategoryNSObject)<TestCategoryProtocol>
@property (nonatomic, copy) NSString *personName;
- (void)TestCategoryMethod;
+ (void)TestCategoryClassMethod;
@end
该分类遵守的协议:
@protocol TestCategoryProtocol <NSObject>
- (void)protocolMethod;
+ (void)protocolClassMethod;
@end
并实现其中的方法:
@implementation TestNSObject (TestCategoryNSObject)
- (void)TestCategoryMethod {
NSLog(@"TestCategoryMethod");
}
+ (void)TestCategoryClassMethod {
NSLog(@"TestCategoryClassMethod");
}
- (void)protocolMethod {
NSLog(@"protocolMethod");
}
+ (void)protocolClassMethod {
NSLog(@"protocolClassMethod");
}
@end
使用clang -rewrite-objc NSObject+TestCategoryNSObject.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_TestNSObject_$_TestCategoryNSObject __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"TestNSObject",
0, // &OBJC_CLASS_$_TestNSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_TestNSObject_$_TestCategoryNSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_TestNSObject_$_TestCategoryNSObject,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_TestNSObject_$_TestCategoryNSObject,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_TestNSObject_$_TestCategoryNSObject,
};
// category结构体数组
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_TestNSObject_$_TestCategoryNSObject,
};
我们能看到,这三个数据就是该类分类的重点!
可能我们看到上面的category结构体的赋值语句其中那几个长串串数据人都傻了,别慌,下面我们慢慢来看:
// 对象方法实现
static void _I_TestNSObject_TestCategoryNSObject_TestCategoryMethod(TestNSObject * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7n_zhjyqbvs75z54mcf1v4rtctr0000gn_T_TestNSObject_TestCategoryNSObject_630503_mi_0);
}
static void _I_TestNSObject_TestCategoryNSObject_protocolMethod(TestNSObject * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7n_zhjyqbvs75z54mcf1v4rtctr0000gn_T_TestNSObject_TestCategoryNSObject_630503_mi_2);
}
// 对象方法列表结构体
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_TestNSObject_$_TestCategoryNSObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"TestCategoryMethod", "v16@0:8", (void *)_I_TestNSObject_TestCategoryNSObject_TestCategoryMethod},
{(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_TestNSObject_TestCategoryNSObject_protocolMethod}}
};
- (void)TestCategoryMethod;
和- (void)protocolMethod;
方法的实现我们可以看到,只要是在该分类中实现的对象方法(不管是协议中的还是自身的),都会添加到该分类的方法列表结构体_OBJC_$_CATEGORY_INSTANCE_METHODS_TestNSObject_$_TestCategoryNSObject
中,如果我们仅仅是对方法定义而不实现,那么它就不会添加进来。
// 类方法的实现
static void _C_TestNSObject_TestCategoryNSObject_TestCategoryClassMethod(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7n_zhjyqbvs75z54mcf1v4rtctr0000gn_T_TestNSObject_TestCategoryNSObject_630503_mi_1);
}
static void _C_TestNSObject_TestCategoryNSObject_protocolClassMethod(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7n_zhjyqbvs75z54mcf1v4rtctr0000gn_T_TestNSObject_TestCategoryNSObject_630503_mi_3);
}
// 类方法列表结构体
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_TestNSObject_$_TestCategoryNSObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"TestCategoryClassMethod", "v16@0:8", (void *)_C_TestNSObject_TestCategoryNSObject_TestCategoryClassMethod},
{(struct objc_selector *)"protocolClassMethod", "v16@0:8", (void *)_C_TestNSObject_TestCategoryNSObject_protocolClassMethod}}
};
+ (void)TestCategoryClassMethod;
和+ (void)protocolClassMethod;
类方法的实现与对象方法一样,但是它会添加到添加到类方法列表结构体_OBJC_$_CATEGORY_CLASS_METHODS_TestNSObject_$_TestCategoryNSObject
中。
// 协议结构体
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]; // 因为我们在定义该协议的时候,有一个的操作,所以这里就会将其加进来,类似于继承吧,所以就有一个数据
} _OBJC_PROTOCOL_REFS_TestCategoryProtocol __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_TestCategoryProtocol __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_TestCategoryProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"protocolClassMethod", "v16@0:8", 0}}
};
// 协议结构体赋值,等等打包给分类
struct _protocol_t _OBJC_PROTOCOL_TestCategoryProtocol __attribute__ ((used)) = {
0,
"TestCategoryProtocol",
(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_TestCategoryProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_TestCategoryProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_TestCategoryProtocol,
0,
0,
0,
sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_TestCategoryProtocol
};
// 将刚赋值完的结构体重命名
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_TestCategoryProtocol = &_OBJC_PROTOCOL_TestCategoryProtocol;
// 将其再打包,封装成一个协议列表结构体,到时候赋值给该分类
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1]; // 因为该分类只遵循了这一个协议,所以是1
} _OBJC_CATEGORY_PROTOCOLS_$_TestNSObject_$_TestCategoryNSObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_TestCategoryProtocol //刚才打包封装的协议的结构体
};
// 属性结构体
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_TestNSObject_$_TestCategoryNSObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"personName","T@\"NSString\",C,N"}} // 两个数据,一个属性名,一个属性类型及其修饰符
};
_ivar_list_t
结构体),它更不会自己生成其set/get
方法。这也说明了Category中不能添加成员变量这一事实。
分类其实也是一个结构体,其中主要包含了以下内容:
注意:_category_t
结构体中并不包含_ivar_list_t
类型,也就是不包含【成员变量结构体】。
我们知道,Objective-C 的运行是依赖 Objective-C 的 Runtime 的,而 Objective-C 的 runtime 和其他系统库一样,是 OS X 和 iOS 通过 dyld 动态加载的。
那么分类到底是怎样将数据在运行期加载到本类上去呢,这就是我们接下来要看的了。
我们先从 runtime 的初始化函数_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();
runtime_init();
exception_init();
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
其调用_dyld_objc_notify_register
方法,传入map_images
地址(方法地址或者函数地址),map_images
读取资源(images代表资源模块):
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
bool takeEnforcementDisableFault;
{
mutex_locker_t lock(runtimeLock);
map_images_nolock(count, paths, mhdrs, &takeEnforcementDisableFault);
}
if (takeEnforcementDisableFault) {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
bool objcModeNoFaults = DisableFaults
|| DisableClassROFaults
|| getpid() == 1
|| is_root_ramdisk()
|| !os_variant_has_internal_diagnostics("com.apple.obj-c");
if (!objcModeNoFaults) {
os_fault_with_payload(OS_REASON_LIBSYSTEM,
OS_REASON_LIBSYSTEM_CODE_FAULT,
NULL, 0,
"class_ro_t enforcement disabled",
0);
}
#endif
}
}
然后在其中调用map_images_nolock
方法(这个方法太长了知道就行),在map_images_nolock
方法中调用_read_images
方法(镜像,加载一些模块),在_read_images
函数中找到与分类相关的代码,加载分类信息(分类信息是个二维数组):
// Discover categories.
for (EACH_HEADER) {
// 获取category列表
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 便利category列表中的每一个category
for (i = 0; i < count; i++) {
// 获得当前category
category_t *cat = catlist[i];
// 获取该category的主类
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;
// 该category有 对象方法 || 协议 || 属性
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
的方法:
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
// 获得一个全局的cats,是一个字典对象,key为cls,value为category_list *列表
NXMapTable *cats = unattachedCategories();
category_list *list;
// 在全局的cats中查找有没有cls对应的category列表
list = (category_list *)NXMapGet(cats, cls);
if (!list) { // 如果没找到
// 新开辟一块空间存储该category列表
list = (category_list *)
calloc(sizeof(*list) + sizeof(list->list[0]), 1);
} else { // 找到了
// 再多开辟一块空间,将传过来的category列表加入其中
list = (category_list *)
realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
}
// 将cat和catHeader打包添加进来
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
// 将我们传递过来的category以字典的方式存储,key为cls,value为传过来的分类列表
NXMapInsert(cats, cls, list);
}
// 返回一个全局的category字典
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,key
为cls,value
为category_list *category_list *list
列表category_list
空间,将这组键(cls)值(cat、catHeader)对插入category_list
大小的空间,将这组键(cls)值(cat、catHeader)对插入等于说这个方法就是将我们分类中的方法添加到一个全局的map中,把类和category做一个关联映射,并且该map的key
为cls
,value
为category_list *
,即一个类对应一个分类列表,一个category_list *
包含了很多分类category_t *
,每个分类category_t *
中又存储了该分类的各种数据(分类名、所属类、对象方法、类方法等等)。
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; // 将属性列表加到proplists属性列表数组中
}
// 取出分类中遵循的协议列表
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) { // 有协议列表
protolists[protocount++] = protolist; // 将协议列表加到protolists协议列表数组中
}
}
// 上面的代码是将分类中存储的方法、属性、协议插入到各自对应的大数组中(方便等等插入对应的本类中的各种列表中),并且从后往前插,保证最终调用方法的时候会最先调用到最新的分类中的方法。
// 取出当前类 cls 的 class_rw_t 数据
auto rw = cls->data();
// 将存储方法、属性、协议数组到本类的 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);
}
class_rw_t
结构体中,就等于说是这个分类现在就没有作用了(也不能这样说,就是这个意思),分类中的方法、属性、协议都已经存储到本类中了,直接就能在本类调用了。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; // 如果原列表中有数据,其长度肯定为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])); // 将新数据插在前面
}
}
那么它是怎么保证新数据一定在旧数据的前面呢?
// memmove :内存移动
void *memmove(void *__dst, const void *__src, size_t __len);
/*
__dst : 移动内存的目的地
__src : 被移动的内存首地址
__len : 被移动的内存长度
将__src的内存移动__len块内存到__dst中
*/
// memcpy :内存拷贝
void *memcpy(void *__dst, const void *__src, size_t __n);
/*
__dst : 拷贝内存的拷贝目的地
__src : 被拷贝的内存首地址
__n : 被移动的内存长度
将__src的内存移动__n块内存到__dst中
*/
memmove
之后:虽然本类的方法,属性,协议列表指针的地址会分别后移,但是本类的对应数组的指针依然指向原始位置:
memcpy
后:原来指针并没有改变,至始至终指向开头的位置。并且经过memmove和memcpy方法之后,分类的方法,属性,协议列表被放在了类对象中原本存储的方法,属性,协议列表前面:
所以总体来说就是先将原方法移动到后面,再将分类方法拷贝进来。
array()->lists
: 类对象原来的方法列表,属性列表,协议列表,比如Person中的那些方法等addedLists
:传入所有分类的方法列表,属性列表,协议列表,比如Person(Eat)、Person(Drink)中的那些方法等。上面代码的作用就是通过memmove将原来的类找那个的方法、属性、协议列表分别进行后移,然后通过memcpy将传入的方法、属性、协议列表填充到开始的位置。
category map
中,以cls
为key
,以category_list *
列表为value
cls
找到全局category map
中对应的分类数据,把分类的数据序列化加入本类中的class_rw_t
结构体中,并且保证新加的数据都是在旧数据的前面的这里需要说明一下,我们的分类为什么不能添加成员变量,这是因为分类结构中并没有成员变量列表的存储属性,分类在运行时被attach
添加到类,是对rw
的处理,在class_rw_t *rw
中,并没有const ivar_list_t *ivars
成员变量列表属性!!!这个成员变量列表属性是在class_ro_t *ro
中处理的!所以我们不能在分类中添加成员变量,但是可以添加属性!!!
直接看结论吧,感觉没啥说的
load方法的调用时机,在runtime加载类、分类时由系统调用的。
每个类、分类的load在程序运行过程中只调用一次load,除非自己再手动调用load才会再次调用。
系统调用load方法它是直接找到类的load方法的地址,然后调用load方法,然后再找到分类的load方法,再去调用。
而对于手动调用load,则通过msgSend方式,找到所属的类的元类对象,如果分类也实现了load,则调用分类的load,因为分类中的方法总是加载本类方法的前面。
对于“覆盖”的方法,会找到最后一个编译的方法,和我们上面理解的分类实现的方法一样。
类第一次接收消息时,会调用initialize(初始化)。
通过msgSend调用。
并且我们在写initialize方法时,不需要调用父类的initialize方法,系统会自动调用,如果你在调用的话,就会多次调用分类的initialize方法了。
load
,当runtime加载类、分类时会调用。load方法总是在main函数之前调用,每个类、分类的load在运行时只调用一次initialize
,在类第一次接收到消息时调用(先初始化父类,再初始化子类,每一个类只会被初始化一次)load方法
:先调用类的load,子类调用load方法之前会先调用父类的load,先编译的先调用;再调用分类的load方法,先编译的先调用initialize方法
:先调用父类的initialize再调用当前类的initialize,如果子类没有实现initialize,则会调用父类的initialize;如果有分类,则调用最后编译的分类的initialize,就不调用本类的initialize了load
,根据IMP地址直接调用(*load_method)(cls, SEL_load)initialize
,通过objc_msgSend进行调用load
是通过直接函数地址调用,只会调用一次initialize
是通过msgSend调用
上面也说了,分类中不可以添加实例变量,但是可以添加属性,但是属性不能自动生成set、get方法,那么我们要怎么实现在分类中添加属性呢,这就需要关联对象出马了!
关联对象也就只有三个接口方法,分别是objc_setAssociatedObject
、objc_getAssociatedObject
和objc_removeAssociatedObjects
。
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
参数一:给哪个对象添加属性,如果要给自己添加属性,使用self就行了
参数二:设置关联对象的key,根据key获取关联对象的属性的值,就跟字典一样
参数三:关联的值,也就是我们想要存储的值
参数四:策略,属性以什么形式保存,类似于修饰符
策略有以下几种:
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 // 指定相关的对象被复制,原子性
};
OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
参数一:获取哪个对象里面的关联的属性
参数二:获取的是那个属性,字典的key,通过key去查找
OBJC_EXPORT void
objc_removeAssociatedObjects(id _Nonnull object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
就一个参数,移除某个对象的全部关联对象。
我们给上面我们说的分类中的@property (nonatomic, copy) NSString *personName;
添加关联对象:
- (void)setPersonName:(NSString *)personName {
objc_setAssociatedObject(self, "personName", personName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)personName {
id temp = objc_getAssociatedObject(self, "personName");
if (temp) {
return temp;
} else {
return nil;
}
}
我们通过personName
作为该对象的key值,通过这个key值就可以存储和访问这个属性存储的值,这样我们也就实现了给分类中添加实例变量的功能了。
关联对象的核心技术有:
直接逐一分析:
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;
}
};
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); }
};
我们发现这个玩意是继承自unordered_map
,也就是说,他也是一个map,并且key
为disguised_ptr_t
,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); }
};
我们发现这个也是一个map,其中key
为void *
,value
为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; }
};
来了来了,终于找到值了,它才是真正存储关联对象值的地方,其中_value
存储的是关联对象的值,_policy
存储的是关联对象的策略。
那接下来我们使用图来总结一下关联对象的结构(这个图真的是太清晰了):
通过对上述结构的了解,我们发现:
AssociationsManager
中object
作为key
,一个被关联对象的所有关联对象都存储在同一个ObjectAssociationMap
中object
被关联的对象不能为nil
value
为nil
,就相当于是移除关联对象key
,获取这个类的所有关联对象
如果有很多类都有关联对象,那么我们就可以通过不同的object来访问各个类中设置的关联对象。
key
,获取这个对象的值
如果该类有很多关联对象,那么我们又可以通过我们自己设置的不同的key值,来访问我们存储的值
分析完了关联对象的底层结构,我们在来看看它的三种方法:
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);
// 如果value存在,就将value和policy进行打包,方便后续的存储
id new_value = value ? acquireValue(value, policy) : nil;
{
// 初始化一个manager
AssociationsManager manager;
// 获取其中的单例AssociationsHashMap,现在就有了全局的关联对象表
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 类型的迭代器
// 以DISGUISE值为key,找这个类的关联对象map
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) { // 没到end,就说明找到了,即存在这个object的关联对象map
// secondary table exists
// 获取到ObjectAssociationMap(key是外部传来的key,value是关联对象类ObjcAssociation)
ObjectAssociationMap *refs = i->second;
// ObjectAssociationMap::iterator 类型的迭代器
// 再以我们自己定义并传进来的key作为key,查找我们保存的对象值
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) { // 如果没到end,即找到了,就说明之前有存储过
// 那么就将原关联对象的值存起来,并且赋新值
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else { // 如果到end了,即没找到,就说明之前没有存储过
// 无该key对应的关联对象,直接赋值保存即可
// ObjcAssociation(policy, new_value)提供了这样的构造函数
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else { // 即第一层遍历到end了,通过object找不到对应的关联对象map
// create the new association (first time).
// 执行到这里,说明该对象是第一次添加关联对象,直接新建添加就完事了
// 初始化ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
// 第一层map赋值
associations[disguised_object] = refs;
// 第二层map赋值
(*refs)[key] = ObjcAssociation(policy, new_value);
// 设置该对象的有关联对象,调用的是setHasAssociatedObjects()方法
object->setHasAssociatedObjects();
}
} else { // 如果封装后的value为nil
// setting the association to nil breaks the association.
// value无值,也就是释放一个key对应的关联对象
// 通过迭代器找对应的map,找不到就说明原来也没有,也就不用管了
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) { // 找到了
// 获取第二层map
ObjectAssociationMap *refs = i->second;
// 通过key在第二层中找到对应的关联对象的值,没找到也就不管了,因为本来也就是要删除这个关联对象的
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); }
这里只对其做了简单的编码处理,直接取反了。
// 获取关联对象的方法
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
AssociationsManager manager;
// 获取到manager中的单例AssociationsHashMap
AssociationsHashMap &associations(manager.associations());
// 获取对象的DISGUISE值
disguised_ptr_t disguised_object = DISGUISE(object);
// 通过对象的DISGUISE值查找对象对应的map
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) { // 找到了
// 获取ObjectAssociationMap,即获取第二层map
ObjectAssociationMap *refs = i->second;
// 在第二层map中通过key查找准确的值
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) { // 找到了
// 获取到关联对象ObjcAssociation
ObjcAssociation &entry = j->second;
// 获取到value
value = entry.value();
// 获取到policy
policy = entry.policy();
// 通过相应的策略返回相对应的修饰属性
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
// 通过相应的策略返回相对应的修饰属性
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
// 返回关联对像的值
return value;
}
很简单,就是通过传入的object
和key
进行两层map的查找,找到了最终再根据策略返回就是了。
// 移除对象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
AssociationsManager manager;
// 获取其中的单例map
AssociationsHashMap &associations(manager.associations());
// 如果map size为空,直接返回
if (associations.size() == 0) return;
// 获取对象的DISGUISE值
disguised_ptr_t disguised_object = DISGUISE(object);
// 通过对象的DISGUISE进行查找
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) { // 找到了
// copy all of the associations that need to be removed.
// 获取第二层map
ObjectAssociationMap *refs = i->second;
// 将第二层map中的内容遍历加入到刚才定义的数组中
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
// 删除这个第二层的map
delete refs;
// 释放第一层的对应键值对
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
// 遍历删除
for_each(elements.begin(), elements.end(), ReleaseValue());
}
到这里就结束了,重点还是那张图,把那张图看懂了,关联对象不轻轻松松学会了。