分类
- 在分类中添加属性会生成对应的成员变量,会生成对应的setter和getter方法的声明,但是不会生成setter和getter方法的实现。
- 分类中的可以写@property,会编译通过,但是引用变量会报错。
- 分类中可以/只能访问原有类中.h中的属性。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问。
- 在本类和分类有同名方法时,优先调用分类的方法。同名方法调用的优先级为分类 > 本类 > 父类。
- 如果多个分类中都有和原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。
- 因为分类是会被子类继承的,所以只对NSObjec添加分类,那么所有的OC类均可以调用我们的扩展方法!
- 对于分类中声明的方法,我们可以不去实现,可以交由子类在需要时进行实现,这点和协议很像。区别是协议是<协议名>,而分类是(分类名)
- OC分类属于Runtime运行时特性,是OC语言独有的创新,其他编程语言所不具备这样的特性! 类扩展属于编译器特性,在编译阶段就会被添加合并到原类中!
- 分类不区分类方法与实例方法,本质类加载过程分类属于插入数据,实例方法插入到类中,类方法插入到元类中,所以自己并不会有分元类。
分类方法不会覆盖掉原来类中的方法,而是共存的。但是分类中的方法在前面,原来的类中的方法在后面,调用的时候,就会调用分类中的方法,如果多个分类有同样的方法,后编译的分类会调用。
分类中不能直接添加成员变量
会直接报错Instance variables may not be placed in categories,成员变量不能定义在分类中
这是因为在分类的源码中:
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);
};
没有存放成员变量的数组,只有属性、协议等
类的加载过程(类的实现)
_objc_init
我们根据之前学的dyld的加载可知:类的加载是在程序启动过程中由dyld动态链接器完成的,在 dyld 完成镜像加载和初始化后,objc_init
会被调用来初始化runtime环境。此时,类和类的方法会被注册到运行时系统中。
void _objc_init(void)
{
// 保证初始化方法只执行一次
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
// 读取影响运行时的环境变量,例如,我们可以根据这个方法打印出那些关键key
environ_init();
// 关于线程key的锁定,tls(thread_local_storage)。例如,每个线程数据的析构函数
tls_init();
// 运行c++静态构造函数。在dyld调用之前,objc自己调用其构造函数
static_init();
// 运行时环境初始化,主要是建表
runtime_init();
// 异常处理初始化
exception_init();
#if __OBJC2__
// 缓存条件初始化
cache_t::init();
#endif
// 启动回调机制,通常不做什么,因为所有初始化都是惰性的
// 但对于某些进程,我们要立马加载trampoline dylib
_imp_implementationWithBlock_init();
// 注册dyld与objc的通知,&map_images,加一个&符号是代表同步进行
// map_images是很耗时的操作,而映射镜像是很重要的必须保证dyld与objc的函数指针同步
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
在这个函数中调用的 runtime_init();
:
void runtime_init(void)
{
// 创建分类的表
objc::unattachedCategories.init(32);
// 创建已分配的类、元类的表
objc::allocatedClasses.init();
}
- 主要是运行时的初始化,开辟类、分类的表空间
- 主要分为两部分:分类初始化、类的表初始化
_dyld_objc_notify_register
这个在刚刚的objc_init中也有见过,它里面的函数是数据的加载的重点:
- map_images:映射镜像,将image镜像文件加载进内存时,会触发该函数,在这个过程中会加载类、分类、协议等数据
- load_images:加载镜像,dyld初始化image会触发该函数,load方法就会在此执行
- unmap_image:dyld将image移除时会触发该函数
map_images
其源码只调用了map_images_nolock
函数。
map_images_nolock
源码调用_read_images
开始读取镜像文件
_read_images(读取镜像文件)
该函数才是map_images流程的核心开始。很重要了,它做了很多事情,去读取mach-o,以header开始进行读取。你可以直接理解为读取镜像文件到内存中去。它对类的处理大致为以下流程:
第一次进来的时候去创建一个类表,该类表包含了类名等(不是runtime的,是全局的已经被加载或没被加载的类)。先将所有的类都加到这个大类表中,并且将类的地址和名字进行绑定,也就是给类赋予名字。然后将混乱错误类处理(就是我们删除的类,但是删除后内存中依旧有这块地址没被清除,我们称为futureClass。)
然后修复一些重映射类,就是重映射没被加载进来的,然后对分类进行处理,但是在启动的时候并不会走,也就是说分类加载并不在这时候。
最后对非懒加载类进行处理,里面包含了类的实现。
功能:
该函数总有这么几大块功能:
- 修复selector引用
- 类的处理
- 协议处理
- 分类的处理
- 非懒加载类的实现(需要设置非懒加载的时候才会进入)
我们在这里主要分析类的处理和非懒加载类的实现。
在类的底层分析中我们得知,类的数据存储在rw中,但是我们从内存获取到的数据是ro,所以需要重新构造一下,将ro数据加到rw和rwe中。
rw, ro, rwe回顾
这三个结构体是影响类的核心所在。
rw本质class_rw_t,dirtyMemory,它包含了ro与rwe,也就是说,我们平时所用到的方法,协议,属性等都是从这里取出来的,类实现的时候默认是ro数据,要记得rw开辟的时候赋值操作。但是为什么说包含rwe呢?
ro本质class_ro_t,cleanMemory,这是类的干净的东西,其编译时就能确定的,例如类名,成员变量,乃至一些方法、属性等,这些存在磁盘中,所以我们看上面篇章的时候类实现都是先从ro入手就是因为这些不可变,存在磁盘。
rwe本质class_rw_ext_t相当于rw的扩展,因为运行时机制我们可以动态添加方法、属性、协议等以及分类操作都会影响类的结构也就是rw,所以在有这些操作的时候系统就会进行开辟rwe,进行创建。但是又不是每一个类都会有上面操作,所以这些类就不会去开辟rwe,避免去染指类的结构
readClass(读取类信息)
在阅读了前面_read_image
的源码后,我们发现它调用了readClass函数。
_read_image
调用readClass函数做了两件事:
- 使用addNameClass函数给类映射类名
- 使用addClassTableEntry函数添加类和元类到所有类的动态表上
那么readClass中是如何实现的呢?
实际上readClass完成这个任务是通过:**一个是将类名与类映射,一个是将类的地址添加到所有类的动态表中 **
源码:
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
// 非懒加载类获取类名的方式,但是其实这里已经都能获取了,可以看里面实现方式是从ro读取的
const char *mangledName = cls->nonlazyMangledName();
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->setSuperclass(nil);
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (mangledName != nullptr) {
// 在这里千万不要以为类实现中的ro赋值于rw的操作是在这。
// 可以自行验证,你会发现无论是系统还是自己创建的都没有走这里
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
// 将类地址与名字绑定起来
addNamedClass(cls, mangledName, replacing);
} else {
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
// 将绑定后的类加到全局大类表中
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
addNamedClass
用来给类添加类名addClassTableEntry
用来添加类或元类到表上
addNamedClass(添加类名)
通过NXMapInsert函数将类名映射到类上,类和类名的映射是通过一个表来关联的。以后可以更方便的通过类名获取到类
/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
给一个需要命名的非元类添加映射关系:name=>cls
如果出现重复的类名,则发出警告,并且使用旧的映射关系
**********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
//在这里将类名添加到类的映射中
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// ASSERT(!cls->isRealized());
}
addClassTableEntry(添加到类动态表)
有一个所有类的动态表,这个表包含了所有的类,现在就将这个类添加到表中,如果是元类也要把元类添加进去
添加类的地址到一个表中,此处也是为了更好的查找类,以后可以直接通过类地址查找类
这里添加的表是之前在objc_init中allocatedClasses开辟的空间。
/***********************************************************************
* addClassTableEntry
* Add a class to the table of all classes. If addMeta is true,
* automatically adds the metaclass of the class as well.
* Locking: runtimeLock must be held by the caller.
将一个类cls添加到所有类的表中
如果addMeta为true,则自动的将其元类也添加进去
**********************************************************************/
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
/*
这个类可以通过共享的内存或数据段成为一个已知的类,但是不可以因为早已加入到动态列表而称为已知类
当然这是因为我们此时要加入,所以也就不能在动态列表中存在了。
*/
/*
得到之前分配的动态列表(在objc_init中分配的)
*/
auto &set = objc::allocatedClasses.get();
ASSERT(set.find(cls) == set.end());
//如果不是一个已知类,也就是表中不存在cls,则将其加入
if (!isKnownClass(cls))
set.insert(cls);
//再继续添加其元类
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
realizeClassWithoutSwift(类列表数据的构造过程)
上面只是将类的名称和地址添加到表中,并没有看到rwe的方法列表、协议列表、属性列表、变量列表的构造过程,现在看下它是什么时候构造的。
realizeClassWithoutSwift
方法就是对于类实现的核心方法了,任何类(懒加载类,非懒加载类)都要经过这个方法的洗礼,才能实现。
其中最关键的就是对rw的赋值等等,以及本类实现,连带着元类,父类,父类元类等都实现
代码结构: 1、设置rw 2、递归实现父类和元类的继承链 3、附着分类数据
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
// 如果类已经实现就返回,证明流程错误,里面有个方法会进行assert
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
//将类的data()从ro变为rw--------------------------------
auto ro = (const class_ro_t *)cls->data();
// 看看是不是元类
auto isMeta = ro->flags & RO_META;
// 如果是futer class,就是在read_images方法的时候那些混乱类
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 此处是类的rw开辟
rw = objc::zalloc<class_rw_t>();
// ro 赋值于rw真正地方
rw->set_ro(ro);
// 标识类实现
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
// 不用说也知道什么意思
cls->setData(rw);
}
//rw里保存的就是数据信息
//ro是保存在rw中的,所以需要赋值给rw
//先将信息从类中获取到,这个类就是macho中读取的类,此时虽然有data(),但这是从MachO中获取的原始数据
//原始数据其实就是ro,这是类本身的数据,也就是干净内存,所以可以通过强转为class_ro_t
//--------------------------------------------------------
// 这是类的缓存初始化,就是消息查找的时候那个cache_t
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 递归实现父类和元类的继承链rw------------------------------
// 去实现它的父类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
// 去实现它的元类
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
//----------------------------------------------------------
// nonpointer_isa,不用关心
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
// 我们看到元类的isa打印是其类的名字,因为这里
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
//如果说你记得上一章我说的环境变量,OBJC_DISABLE_NONPOINTER_ISA
// 所以nonpointer_isa标识就是末尾为1,下面只是去设置
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
// 看这里根据环境变量去设置。不需要关心
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
// 设置其父类,以及isa指向元类
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
// 添加关系,如果有父类
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
//附着类别数据rw-----------------------------------------
// 不要关心参数previously,其实它可以当做是备用参数,对我们来讲无实际意义
methodizeClass(cls, previously);
//对类的rwe进行设置
//将分类信息添加到rwe中
//肃然这里注释写的是附着分类数据,其实rwe中也会把类的数据也就是ro添加到rwe中
//----------------------------------------------------------
return cls;
}
小结:
- 加载到内存中的原始的类的数据格式并不是我们所需要的格式,所以就需要重新设置,其实设置的就是rw
- 类的实现其实就是将类的bits中的data进行重新赋值,基本上就是给rw进行赋值,包括ro和rwe
- 而这个data()就是rw,rw包含ro和rwe
- ro是类本身的数据,包括方法列表、属性列表、协议列表、成员变量列表
- rwe是分类以及运行时改变的数据,包括方法列表、属性列表、协议列表,还有ro
methodizeClass(附着分类、修复类)
代码结构:1、方法排序;2、添加ro数据到rwe中;3、附着分类数据到rwe中
- 先获取到ro的基本方法列表
- 通过prepareMethodLists进行排序,这样方法查找时可以用二分查找法来查找imp
- 通过attachLists将ro的数据附着到rwe上
- 属性和协议列表也通过attachLists附着到rwe上
- 上面是把ro的列表加到了rwe中,还需要通过objc::unattachedCategories.attachToClass将分类的方法列表加到rwe中
- 列表有三种需要操作的,方法列表、协议列表、属性列表(变量列表不存放到rwe中,所以不需要进行操作)
attachToClass(获取分类附着到类上)
将分类或运行时创建的数据添加到类的rwe上
- 这里通过get()拿到的是分类中的数据
- load_images函数将分类数据加载后存放到数组中,在类进行加载时直接拿过来附着
- 这里调用attachCategories函数传入的分类是这个类当前可以加载的所有分类。
总结

- 类加载到内存中,需要重新构造类,构造的就是rw,rw包括rwe和ro
- 类的处理包括给类映射类名、将类和元类添加到所有类的表中
- rw的构造过程就是给rw添加ro,并且生成rwe
- rwe的生成过程是先添加ro的数据,之后获取到分类数据,添加到rwe中
- ro以及分类中的方法列表均要按照方法选择地址排序,方便后续的二分查找法
- 在将方法列表添加到类的rwe时,需要倒序插入。后加载分类的在前面,后加载的分类在后面,类的数据在最后。
load的加载过程
类load表:loadable_class
struct loadable_class {
Class cls; // may be nil
IMP method;
};
分类load表:loadable_category
struct loadable_category {
Category cat; // may be nil
IMP method;
};
load_images(加载镜像)
该方法做了两件事,一个是分类的加载,一个是load的加载和调用
void load_images(const char *path __unused, const struct mach_header *mh)
{
// 当完成dyld通知注册回调后,这里看函数名意思是加载所有的分类,但是是这样吗?
//
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// 去准备分类方法,看备注就知道
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// 这个没什么看的,就是去调用load方法了,但是先调用类的,再按顺序调用分类的
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}

分类的加载过程
分类的加载方法load_categories_nolock
static void load_categories_nolock(header_info *hi) {
// 判断是否存在预优化的类别
bool hasPreoptimizedCategories = hi->info()->dyldCategoriesOptimized() && !DisablePreattachedCategories;
// 判断是否存在被覆盖的共享缓存镜像
bool hasRoot = dyld_shared_cache_some_image_overridden();
// 判断类别是否包含类属性
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
// 定义处理类别列表的lambda表达式
auto processCatlist = [&](category_t * const *catlist, bool stubCategories) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
if (!cls) {
// 类别的目标类丢失(可能是弱链接),忽略该类别
if (PrintConnecting) {
_objc_inform("CLASS: 忽略类别 \?\?\?(%s) %p 目标类丢失", cat->name, cat);
}
continue;
}
// 处理当前类别
if (cls->isStubClass()) {
// 如果是Stub类,类别添加到未关联的类别列表中
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
// 将类别注册到目标类中
if (!didInitialAttachCategories && hasPreoptimizedCategories &&
objc::inSharedCache((uintptr_t)cls) &&
objc::inSharedCache((uintptr_t)cls->safe_ro()))
continue;
//将分类附加到一个类或其元类时所采取的步骤。
if (cat->instanceMethods || cat->protocols || cat->instanceProperties) {
if (cls->isRealized()) {
if (slowpath(PrintConnecting))
_objc_inform("CLASS: 附加类别 (%s) %p 到类 %s", cat->name, cat, cls->nameForLogging());
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
if (slowpath(PrintConnecting))
_objc_inform("CLASS: 添加未关联的类别 (%s) %p 到类 %s", cat->name, cat, cls->nameForLogging());
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) {
if (cls->ISA()->isRealized()) {
if (slowpath(PrintConnecting))
_objc_inform("CLASS: 附加类别 (%s) %p 到元类 %s", cat->name, cat, cls->nameForLogging());
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
if (slowpath(PrintConnecting))
_objc_inform("CLASS: 添加未关联的类别 (%s) %p 到元类 %s", cat->name, cat, cls->nameForLogging());
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
// 判断是否需要扫描类别列表
bool scanCatlist = didInitialAttachCategories || !hasPreoptimizedCategories || hasRoot;
if (scanCatlist) {
if (slowpath(PrintPreopt)) {
_objc_inform("预优化:扫描镜像 %p %s 中的类别 "
"- didInitialAttachCategories=%d "
"hi->info()->dyldCategoriesOptimized()=%d hasRoot=%d",
hi->mhdr(), hi->fname(), didInitialAttachCategories,
hi->info()->dyldCategoriesOptimized(), hasRoot);
}
processCatlist(hi->catlist(&count), /*stubCategories*/false);
} else {
if (slowpath(PrintPreopt)) {
_objc_inform("预优化:忽略镜像 %p %s 中的预优化类别", hi->mhdr(), hi->fname());
}
}
// 无论如何都要扫描指向stub类的类别列表,因为这些类别不是预优化的
processCatlist(hi->catlist2(&count), /*stubCategories*/true);
}
这段代码首先判断了是否存在经过dyld(动态链接器)优化的类别、共享缓存镜像中是否有被覆盖的类、类别是否包含类属性。
然后定义Lambda函数用于处理类别列表,遍历类别链表,如果类已实现,则使用attachCategories将分类附加到这个类上,同时按需要重建类的方法列表、属性列表等。
如果目标类还没有实现,无法直接附加分类。这时,将这个分类信息保存到 objc::unattachedCategories
中,以便在类实现之后再附加。
从上面的代码我们可以知道:分类的加载的核心代码是attachCategories
和attachToClass
attachCategories
attachCategories
的作用是将分类的方法、属性和协议附加到目标类或元类上:
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
// 如果开启了 PrintReplacedMethods 选项,则打印替换的分类信息
if (slowpath(PrintReplacedMethods)) {
printReplacements(cls, cats_list, cats_count);
}
// 如果开启了 PrintConnecting 选项,则打印附加分类的信息
if (slowpath(PrintConnecting)) {
_objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
for (uint32_t i = 0; i < cats_count; i++)
_objc_inform(" category: (%s) %p", cats_list[i].cat->name, cats_list[i].cat);
}
/*
* 在启动期间,只有少数类有超过 64 个分类。
* 这使用了少量的栈空间,并避免了 malloc。
*
* 分类必须以正确的顺序添加,即从后到前。
* 为了做到这一点,我们从前到后迭代 cats_list,构建局部缓冲区,然后
* 在 chunks 上调用 attachLists。attachLists 会将列表添加到前面,因此最终结果
* 是预期的顺序。
*/
constexpr uint32_t ATTACH_BUFSIZ = 64; // 定义缓冲区大小
struct Lists {
ReversedFixedSizeArray<method_list_t *, ATTACH_BUFSIZ> methods; // 存储方法列表
ReversedFixedSizeArray<property_list_t *, ATTACH_BUFSIZ> properties; // 存储属性列表
ReversedFixedSizeArray<protocol_list_t *, ATTACH_BUFSIZ> protocols; // 存储协议列表
};
Lists preattachedLists; // 预附加分类的列表
Lists normalLists; // 正常分类的列表
bool fromBundle = NO; // 标记是否来自 bundle
bool isMeta = (flags & ATTACH_METACLASS); // 标记是否为元类
auto rwe = cls->data()->extAllocIfNeeded(); // 获取类的扩展数据
// 遍历分类列表
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta); // 获取分类的实例方法列表或类方法列表
bool isPreattached = entry.hi->info()->dyldCategoriesOptimized() && !DisablePreattachedCategories; // 判断是否为预附加分类
Lists *lists = isPreattached ? &preattachedLists : &normalLists; // 根据是否为预附加分类选择列表
if (mlist) {
if (lists->methods.isFull()) {
prepareMethodLists(cls, lists->methods.array, lists->methods.count, NO, fromBundle, __func__); // 准备方法列表
rwe->methods.attachLists(lists->methods.array, lists->methods.count, isPreattached, PrintPreopt ? "methods" : nullptr); // 附加方法列表
lists->methods.clear(); // 清空方法列表
}
lists->methods.add(mlist); // 添加方法列表到列表中
fromBundle |= entry.hi->isBundle(); // 更新是否来自 bundle 标记
}
property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); // 获取分类的属性列表
if (proplist) {
if (lists->properties.isFull()) {
rwe->properties.attachLists(lists->properties.array, lists->properties.count, isPreattached, PrintPreopt ? "properties" : nullptr); // 附加属性列表
lists->properties.clear(); // 清空属性列表
}
lists->properties.add(proplist); // 添加属性列表到列表中
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta); // 获取分类的协议列表
if (protolist) {
if (lists->protocols.isFull()) {
rwe->protocols.attachLists(lists->protocols.array, lists->protocols.count, isPreattached, PrintPreopt ? "protocols" : nullptr); // 附加协议列表
lists->protocols.clear(); // 清空协议列表
}
lists->protocols.add(protolist); // 添加协议列表到列表中
}
}
// 附加分类的辅助函数
auto attach = [&](Lists *lists, bool isPreattached) {
if (lists->methods.count > 0) {
prepareMethodLists(cls, lists->methods.begin(), lists->methods.count,
NO, fromBundle, __func__); // 准备方法列表
rwe->methods.attachLists(lists->methods.begin(), lists->methods.count, isPreattached, PrintPreopt ? "methods" : nullptr); // 附加方法列表
if (flags & ATTACH_EXISTING) {
flushCaches(cls, __func__, [](Class c){
// 常量缓存已经在 prepareMethodLists 中处理了
// 如果类仍然是常量的,这里保留
return !c->cache.isConstantOptimizedCache();
});
}
}
rwe->properties.attachLists(lists->properties.begin(), lists->properties.count, isPreattached, PrintPreopt ? "properties" : nullptr); // 附加属性列表
rwe->protocols.attachLists(lists->protocols.begin(), lists->protocols.count, isPreattached, PrintPreopt ? "protocols" : nullptr); // 附加协议列表
};
// 附加预附加分类和正常分类
attach(&preattachedLists, true);
attach(&normalLists, false);
}
这段代码首先定义用于存储分类中方法、属性和协议的缓冲区。
然后遍历传入的分类列表,对于每个分类,提取方法、属性和协议列表。根据是否为预附加分类选择合适的缓冲区 (preattachedLists 或 normalLists)。如果缓冲区满了,则调用 prepareMethodLists 函数整理缓冲区中的方法列表(比如排序、去重),然后调用rwe->methods.attachLists
方法直接将整理好的方法列表附加到类的扩展数据rwe中。然后再清空缓冲区,准备放入后面的分类数据。
分类的懒加载类和非懒加载类
- 主类是非懒加载,其分类也会标记为非懒加载,提前进行加载
- 如果分类是非懒加载,会促使主类也变为非懒加载
- 如果主类和分类都是懒加载,则只会在第一次调用方法时加载
注:这里说的类的加载指构造rwe。

分类的使用场合
- ① 给一个类添加新的方法,可以为系统的类扩展功能。
- ② 分解体积庞大的类文件,可以将一个类按功能拆解成多个模块,方便代码管理。
- ③ 创建对私有方法的前向引用:声明私有方法,把 Framework 的私有方法公开等。直接调用其他类的私有方法时编译器会报错的,这时候可以创建一个该类的分类,在分类中声明这些私有方法(不必提供方法实现),接着导入这个分类的头文件就可以正常调用这些私有方法。
- ④ 向对象添加非正式协议:创建一个 NSObject 或其子类的分类称为 “创建一个非正式协议”。 (正式协议是通过 protocol 指定的一系列方法的声明,然后由遵守该协议的类自己去实现这些方法。而非正式协议是通过给 NSObject 或其子类添加一个分类来实现。非正式协议已经渐渐被正式协议取代,正式协议最大的优点就是可以使用
泛型
约束,而非正式协议不可以。)
分类总结
- 对于运行时加载的分类情况:1.类与分类都实现load方法;2.分类有两个以上实现load方法
- 对于编译时加载的分类情况:1.类实现load方法,分类没有;2.类没有实现,分类仅有一个实现;3.类与分类均没实现
所以希望不要动不动就实现load方法,很耗时,除了一些必要的操作。我们可以去执行initialize方法。
Q&A
Q:分类的对象方法,类方法都存在哪里?
一个类的所有分类的对象方法放在类对象中,所有分类的类方法存放在元类中
Q:分类的方法是如何添加到类对象方法列表中的?
大概流程
- 通过Runtime加载某个类的所有Category数据
- 把所有Category的方法、属性、协议数据,合并到一个大数组中
- 后面参与编译的Category数据,会在数组的前面
- 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
Q:Category的实现原理
Category编译之后的底层结构是struct category_t
,里面存储着分类的对象方法、类方法、属性、协议信息
在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
Q:Category和Class Extension的区别是什么?
Class Extension在编译的时候,它的数据就已经包含在类信息中
Category是在运行时,才会将数据合并到类信息中
Q:+load方法调用顺序?
- 先调用类的+load方法
1.1按照编译先后顺序调用(先编译,先调用)
1.2先调用父类的+load再调用子类的+load
2.** 再调用分类的+load方法**
2.1按照编译先后顺序调用(先编译,先调用)
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
- 每个类、分类的+load,在程序运行过程中只调用一次,只有在加载类时候调用一次
- 不存在分类的+load方法覆盖类的+load方法
Q:load、initialize的调用顺序?
-
load
1) 先调用类的load
a) 先编译的类,优先调用load
b) 调用子类的load之前,会先调用父类的load
2) 再调用分类的load
a) 先编译的分类,优先调用load -
initialize
1> 先初始化父类
2> 再初始化子类(可能最终调用的是父类的initialize方法)
关联对象实现原理
实现关联对象技术的核心对象有
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
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
方法:
_object_set_associative_reference
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// 这段代码用于处理当 object 和 key 为 nil 时的情况。一些代码可能依赖于此不会崩溃。
// 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));
// 将 object 转换为 DisguisedPtr 类型。
DisguisedPtr<objc_object> disguised{(objc_object *)object};
// 根据策略和值创建关联对象。
//四个核心对象之一ObjcAssociation
ObjcAssociation association{policy, value};
// 在锁外保留新值(如果有)。
association.acquireValue();
bool isFirstAssociation = false;
{
// 管理关联对象的管理器。
//四个核心对象之一AssociationsManager
AssociationsManager manager;
// 获取关联对象的哈希表。
//四个核心对象之一AssociationsHashMap
AssociationsHashMap &associations(manager.get());
if (value) {
// 尝试在哈希表中插入关联对象,如果是首次插入,则返回 true。
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});//四个核心对象之一AssociationsHashMap
if (refs_result.second) {
/* 这是我们第一次建立关联 */
isFirstAssociation = true;
}
/* 建立或替换关联对象 */
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;
// 查找指定 key 的关联对象。
auto it = refs.find(key);
if (it != refs.end()) {
// 交换并移除旧的关联对象。
association.swap(it->second);
refs.erase(it);
// 如果没有关联对象了,则移除对象的关联对象。
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// 在锁外调用 setHasAssociatedObjects,因为这将调用对象的 _noteAssociatedObjects 方法,
// 这可能会触发 +initialize 方法,可能会做任意操作,包括设置更多的关联对象。
if (isFirstAssociation)
object->setHasAssociatedObjects();
// 释放旧值(在锁外)。
association.releaseHeldValue();
}
下面来逐步分析四个核心对象:
AssociationsManager和AssociationsHashMap
class AssociationsManager {
//声明一个映射值为ObjectAssociationMap的_mapStorage成员变量
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
// 构造函数,在对象创建时调用,锁定全局锁
AssociationsManager() { AssociationsManagerLock.lock(); }
// 析构函数,在对象销毁时调用,解锁全局锁
~AssociationsManager() { AssociationsManagerLock.unlock(); }
// 返回存储对象的引用
AssociationsHashMap &get() {
return _mapStorage.get();
}
// 初始化存储对象的静态方法
static void init() {
_mapStorage.init();
}
};
AssociationsHashMap
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
可以看出,在AssociationsManager
内部有一个哈希表AssociationsHashMap
,在这个哈希表中,键是objc_object
(即我们传入的object对象),值是ObjectAssociationMap
。
ObjcAssociation
class ObjcAssociation {
uintptr_t _policy;//策略
id _value; //value值
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
ObjcAssociation(const ObjcAssociation &other) = default;
ObjcAssociation &operator=(const ObjcAssociation &other) = default;
ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
swap(other);
}
inline void swap(ObjcAssociation &other) {
std::swap(_policy, other._policy);
std::swap(_value, other._value);
}
inline uintptr_t policy() const { return _policy; }
inline id value() const { return _value; }
inline void acquireValue() {
if (_value) {
switch (_policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
_value = objc_retain(_value);
break;
case OBJC_ASSOCIATION_SETTER_COPY:
_value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
break;
}
}
}
inline void releaseHeldValue() {
if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
objc_release(_value);
}
}
inline void retainReturnedValue() {
if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
objc_retain(_value);
}
}
inline id autoreleaseReturnedValue() {
if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
return objc_autorelease(_value);
}
return _value;
}
};
在ObjcAssociation
中,保存了我们传入的策略_policy
和value值_value
,并提供了方法来保留、释放、复制和自动释放这些对象。通过使用这个类,可以安全地管理关联对象的生命周期,并确保在不同操作下的一致性。
_object_set_associative_reference的流程
_object_set_associative_reference
的代码中还调用了一个acquireValue
函数,这个函数是用于通过对策略的判断返回不同的值:
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;
}
在_object_set_associative_reference
的代码中:
首先根据我们传入的value经过acquireValue
函数处理返回了new_value。
之后创建AssociationsManager
manager,得到manager内部的AssociationsHashMap
即associations
。
之后我们看到了我们传入的第一个参数object
经过DISGUISE
函数被转化为了disguised_ptr_t
类型的disguised_object
。
如果传入的value为空,那么就删除这个关联对象;如果传入的value不为空,那么就尝试在哈希表中插入关联对象,如果是首次插入,则返回 true。
在哈希表插入关联对象的流程:
try_emplace
在哈希表插入关联对象用到了auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
这句代码,我们来看看try_emplace
:
template <typename... Ts>
std::pair<iterator, bool> try_emplace(KeyT &&Key, Ts &&... Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
false); // Already in map.
// Otherwise, insert the new element.
TheBucket =
InsertIntoBucket(TheBucket, std::move(Key), std::forward<Ts>(Args)...);
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
true);
}
template<typename LookupKeyT>
bool LookupBucketFor(const LookupKeyT &Val,
const BucketT *&FoundBucket) const {
const BucketT *BucketsPtr = getBuckets();
const unsigned NumBuckets = getNumBuckets();
if (NumBuckets == 0) {
FoundBucket = nullptr;
return false;
}
首先创建一个空的桶 TheBucket。桶用于在哈希表中查找或存储键值对。
调用 LookupBucketFor
,尝试在哈希表中查找与传入的 Key
对应的桶。如果找到了桶(即键已经存在),则返回指向该桶的迭代器和 false,表示未添加新的元素。
如果哈希表中没有找到对应的桶,则调用 InsertIntoBucket
将新创建的空桶 TheBucket 存入 AssociationsHashMap
中。(此时还没有存入policy
和value
,它们在assocition
对象中)
然后调用refs.try_emplace
将 association
(包含 policy 和 value)与 Key
关联起来,存入 refs
。Key存bucket的first,value存second。
- 关联对象并不存储在被关联对象本身内存中,而是有一个全局统一的 AssociationsManager中
- 一个实例对象就对应一个ObjectAssociationMap,
- 而ObjectAssociationMap中存储着多个此实例对象的关联对象的key以及ObjcAssociation,
- ObjcAssociation中存储着关联对象的value和policy策略
objc_getAssociatedObject
objc_getAssociatedObject
用于取出关联对象对应的值_value。
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
id
_object_get_associative_reference(id object, const void *key)
{
// 创建一个默认构造的 ObjcAssociation 对象
ObjcAssociation association{};
{
// 创建 AssociationsManager 对象,管理关联对象
AssociationsManager manager;
// 获取 AssociationsHashMap 的引用
AssociationsHashMap &associations(manager.get());
//在associations哈希表中查找给定对象object的关联对象
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
// 找到对象的关联对象后,获取 ObjectAssociationMap
ObjectAssociationMap &refs = i->second;
// 在ObjectAssociationMap中查找指定键的关联对象
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
// 找到关联对象后,保存其值到 association
association = j->second;
// 保留关联对象的返回值
association.retainReturnedValue();
}
}
}
// 返回与键关联的对象,并自动释放
return association.autoreleaseReturnedValue();
}
retainReturnedValue
是ObjcAssociation
中的一个内联函数:
inline void retainReturnedValue() {
if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
//增加 _value 对象的引用计数
objc_retain(_value);
}
}
autoreleaseReturnedValue
也是ObjcAssociation
中的一个内联函数:
inline id autoreleaseReturnedValue() {
if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
//标记_value自动释放
return objc_autorelease(_value);
}
//返回_value
return _value;
}
这段代码中传入的值为我们要访问的关联对象object
和用于在AssociationsHashMap
中获取对应value的key
。
首先获取AssociationsManager
中的AssociationsHashMap
,然后在AssociationsHashMap
中查找给定对象object
的关联对象,找到后获取其对应的ObjectAssociationMap
,然后在ObjectAssociationMap
中查找传入的key
的关联对象,找到后将其值保存到association
并调用retainReturnedValue
增加_value
的引用计数。最后返回与键关联的对象的_value
,并自动释放。
objc_removeAssociatedObjects
objc_removeAssociatedObjects
函数用来删除所有关联对象,内部调用了_object_remove_assocations
:
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_associations(object, /*deallocating*/false);
}
}
void _object_remove_associations(id object, bool deallocating)
{
// 创建一个 ObjectAssociationMap 对象,用于存储关联对象
ObjectAssociationMap refs{};
{
// 创建 AssociationsManager 对象,管理关联对象
AssociationsManager manager;
// 获取 AssociationsHashMap 的引用
AssociationsHashMap &associations(manager.get());
// 在 associations 哈希表中查找给定对象 object 的关联对象
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
// 找到对象的关联对象后,将其内容交换到 refs 中
refs.swap(i->second);
// 如果不是在销毁过程中,SYSTEM_OBJECT 关联对象会被保留
bool didReInsert = false;
if (!deallocating) {
// 遍历所有关联对象
for (auto &ref: refs) {
// 检查是否是 SYSTEM_OBJECT 类型的关联对象
if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
// 重新插入到哈希表中
i->second.insert(ref);
didReInsert = true;
}
}
}
// 如果没有重新插入任何对象,则从哈希表中删除该对象的关联
if (!didReInsert)
associations.erase(i);
}
}
// 在正常释放后需要释放的关联对象
SmallVector<ObjcAssociation *, 4> laterRefs;
// 释放所有的关联对象(在锁外进行)
for (auto &i: refs) {
// 检查是否是 SYSTEM_OBJECT 类型的关联对象
if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
// 如果不是在销毁过程中,RELEASE_LATER 类型的关联对象不会被释放
if (deallocating)
laterRefs.append(&i.second);
} else {
// 释放关联对象持有的值
i.second.releaseHeldValue();
}
}
// 对于需要稍后释放的对象,进行释放操作
for (auto *later: laterRefs) {
later->releaseHeldValue();
}
}
在这段代码中:
首先创建一个 ObjectAssociationMap
类型的局部变量 refs。这个变量用于暂时保存和处理与对象关联的所有关联对象。然后获取AssociationsManager
中的AssociationsHashMap
,然后在AssociationsHashMap
中查找给定对象object
的关联对象,找到后获取其对应的ObjectAssociationMap
,然后将其内容交换到 refs 中,然后在refs中遍历所有的关联对象,检查遍历到的对象有没有SYSTEM_OBJECT
策略,如果有,将其重新插入到 associations 哈希表中;如果没有,就从哈希表中删除该对象的关联。然后在锁外释放所有的关联对象。如果不是在销毁过程中,RELEASE_LATER
类型的关联对象不会被释放。
总的来说
关联对象流程
设值:
- 创建一个 AssociationsManager 管理类
- 获取唯一的全局静态哈希Map
- 判断是否插入的关联值是否存在:
- 存在走第4步
- 不存在:关联对象插入空流程
- 创建一个空的 ObjectAssociationMap 去取查询的键值对
- 如果发现没有这个key就插入一个空的BucketT进去,然后返回
- 标记对象存在关联对象
- 用当前修饰策略(policy)和值(value)组成了一个ObjcAssociation替换原来BucketT中的空
- 标记一下 ObjectAssociationMap的第一次为false
取值:
- 创建一个 AssociationsManager 管理类
- 获取唯一的全局静态哈希表AssociationsHashMap
- 根据DisguisedPtr找到AssociationsHashMap中的 iterator迭代查询器
- 如果这个迭代查询器不是最后一个 获取:ObjectAssociationMap(这里有policy和value)
- 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
- 返回value