Category 编译之后的底层结构是 struct category_t
,里面存储着分类的对象方法、类方法、属性、协议信息,在程序运行的时候,runtime 会将 Category 的数据,合并到类信息中。
struct category_t {
const char *name; // 分类的名字
classref_t cls; // 分类所属的类
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods; // 对象方法列表
WrappedPtr<method_list_t, PtrauthStrip> 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);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
Category的加载处理过程
_objc_init
map_images
map_images_nolock
_read_images
remethodizeClass
attachCategories
attachLists
- 通过Runtime加载某个类的所有Category数据。
- 把所有Category的方法、属性、协议数据,合并到一个大数组中。后面参与编译的Category数据,会在数组的前面 。
- 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面。
+ load方法
_objc_init
load_images
prepare_load_methods
schedule_class_load
:schedule_class_load(cls->getSuperclass());
递归调用, 先添加父类,再添加自己,所以会先调用类的 +load 方法,然后才调用自己的 +load 方法。
add_class_to_loadable_list
:将 cls
添加到 loadable_classes
数组的最后面
add_category_to_loadable_list
call_load_methods
:调用 +load
方法
call_class_loads
:调用类的 +load
方法
call_category_loads
:调用分类的 +load
方法
(*load_method)(cls, @selector(load))
// 取出类的 +load 方法。load_method指向+load方法内存地址
load_method_t load_method = (load_method_t)classes[i].method;
// 利用函数地址直接调用+load方法
(*load_method)(cls, @selector(load));
+load
方法是根据方法地址直接调用,并不是经过 objc_msgSend 函数调用。
+load
方法会在 runtime 加载类、分类时调用。每个类、分类的 +load
,在程序运行过程中只调用一次
调用顺序:
- 先调用类的
+load
。按照编译先后顺序调用(先编译,先调用),调用子类的+load
之前会先调用父类的+load
。 - 再调用分类的
+load
。按照编译先后顺序调用(先编译,先调用)。
+initialize方法
+initialize
方法会在类第一次接收到消息时调用。
先调用父类的 +initialize
,再调用子类的 +initialize
(先初始化父类,再初始化子类,每个类只会初始化1次)
-
+initialize
是通过objc_msgSend
进行调用的 - 如果子类没有实现
+initialize
,会调用父类的+initialize
(所以父类的+initialize可能会被调用多次) - 如果分类实现了
+initialize
,就覆盖类本身的+initialize
调用
objc_msgSend
(objc-msg-arm64.s
文件内)
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
_class_initialize
callInitialize
objc_msgSend(cls, SEL_initialize)
关联对象
不能直接给 Category 添加成员变量,但是可以间接实现 Category 有成员变量的效果。
关联对象由 AssociationsManager
管理并在 AssociationsHashMap
存储,所有对象的关联内容都在同一个全局容器中。
关联对象相关API:
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
objc_removeAssociatedObjects(id _Nonnull object)
使用方法:
// #import <objc/runtime.h>
- (void)setValue:(int)value {
// 添加关联对象
objc_setAssociatedObject( self, @selector(value), @(value), OBJC_ASSOCIATION_COPY);
}
- (int)value {
// 获得关联对象
// 隐式参数 _cmd == @selector(value)
return [objc_getAssociatedObject(self, _cmd) intValue];
}
objc_AssociationPolicy:
objc_AssociationPolicy | 对应的修饰符 |
---|---|
OBJC_ASSOCIATION_ASSIGN | assign |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | strong, nonatomic |
OBJC_ASSOCIATION_COPY_NONATOMIC | copy, nonatomic |
OBJC_ASSOCIATION_RETAIN | strong, atomic |
OBJC_ASSOCIATION_COPY | copy, atomic |
实现关联对象技术的核心对象有:
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation


class AssociationsManager {
AssociationsHashMap &get() {
return _mapStorage.get();
}
};
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
class ObjcAssociation {
uintptr_t _policy;
id _value;
};
源码跟踪:
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
id
_object_get_associative_reference(id object, const void *key)
{
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
......
}
网友评论