-
Category
的本质是一个Category_t 的结构体
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);
//如果是元类,返回nil;否则返回协议列表
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
分类的加载流程
-
_objc_init
objc
的入口,进行了一些初始化操作,注册了镜像状态改变时的回调函数 -
map_images
主要是加锁并调用map_images_nolock
-
map_images_nolock
在这个函数中,完成所有类的注册、fixup
等工作,还有初始化自动释放池、初始化Sidetable
,调用+load
方法等工作并在函数后端调用了_read_images
-
_read_images
把category
的实例方法、协议以及属性添加到类上
把category
的类方法和协议添加到类的metaclass上 load_categories_nolock
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
// Process this category.
if (cls->isStubClass()) {
// Stub classes are never realized. Stub classes
// don't know their metaclass until they're
// initialized, so we have to add categories with
// class methods or properties to the stub itself.
// methodizeClass() will find them and add them to
// the metaclass as appropriate.
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
processCatlist(_getObjc2CategoryList(hi, &count));
processCatlist(_getObjc2CategoryList2(hi, &count));
}
attachCategories
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
/*
* Only a few classes have more than 64 categories during launch.
* This uses a little stack, and avoids malloc.
*
* Categories must be added in the proper order, which is back
* to front. To do that with the chunking, we iterate cats_list
* from front to back, build up the local buffers backwards,
* and call attachLists on the chunks. attachLists prepends the
* lists, so the final result is in the expected order.
*/
//1.在堆上创建方法、属性、协议数组,用来存储分类的方法、属性、协议
constexpr uint32_t ATTACH_BUFSIZ = 64;
method_list_t *mlists[ATTACH_BUFSIZ];
property_list_t *proplists[ATTACH_BUFSIZ];
protocol_list_t *protolists[ATTACH_BUFSIZ];
//2.遍历 cats ,取出各个分类的方法、属性、协议,并填充到上述代码创建的数组中
uint32_t mcount = 0;// 记录方法的数量
uint32_t propcount = 0;// 记录属性的数量
uint32_t protocount = 0; // 记录协议的数量
bool fromBundle = NO;// 记录是否是从 bundle 中取的
bool isMeta = (flags & ATTACH_METACLASS);
//3.取出 cls 的 class_rw_t 数据
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);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
//将方法列表放入 mlists 方法列表数组中
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
//分类的头部信息中存储了是否是 bundle,将其记住
fromBundle |= entry.hi->isBundle();
}
// 取出分类中的属性列表,如果是元类,取得是nil
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
// 将属性列表放入 proplists 属性列表数组中
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
// 取出分类中遵循的协议列表
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
// 将协议列表放入 protolists 协议列表数组中
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
//4.存储方法、属性、协议数组到 rw
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
// 将方法列表添加到 rw 中的方法列表数组中
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
// 将新属性列表添加到 rw 中的属性列表数组中
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
//将新协议列表添加到 rw 中的协议列表数组中
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
@interface Teacher (English)
@property (nonatomic, copy) NSString *country;
@property (nonatomic, copy) NSString *schoolName;
- (void)start;
- (void)end;
+ (void)run;
@end
@implementation Teacher (English)
- (void)start{
NSLog(@"%s", __func__);
}
- (void)end{
NSLog(@"%s", __func__);
}
+ (void)run{
NSLog(@"%s", __func__);
}
@end
// @implementation Teacher (English)
static void _I_Teacher_English_start(Teacher * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9t_267lzq1s59974rx5dhk3c2380000gn_T_Teacher_English_1e08a7_mi_0, __func__);
}
static void _I_Teacher_English_end(Teacher * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9t_267lzq1s59974rx5dhk3c2380000gn_T_Teacher_English_1e08a7_mi_1, __func__);
}
static void _C_Teacher_English_run(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9t_267lzq1s59974rx5dhk3c2380000gn_T_Teacher_English_1e08a7_mi_2, __func__);
}
// @end
struct _prop_t {
const char *name;
const char *attributes;
};
struct _protocol_t;
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};
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;
};
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
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;
};
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
//分类的实例方法列表
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_Teacher_$_English __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"start", "v16@0:8", (void *)_I_Teacher_English_start},
{(struct objc_selector *)"end", "v16@0:8", (void *)_I_Teacher_English_end}}
};
//分类的类方法列表
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_Teacher_$_English __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"run", "v16@0:8", (void *)_C_Teacher_English_run}}
};
//分类属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_Teacher_$_English __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"country","T@\"NSString\",C,N"},
{"schoolName","T@\"NSString\",C,N"}}
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_Teacher;
//分类的结构体 这是系统在编译时实例化 _category_t 生成的
static struct _category_t _OBJC_$_CATEGORY_Teacher_$_English __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Teacher",
0, // &OBJC_CLASS_$_Teacher,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Teacher_$_English,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Teacher_$_English,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Teacher_$_English,
};
static void OBJC_CATEGORY_SETUP_$_Teacher_$_English(void ) {
_OBJC_$_CATEGORY_Teacher_$_English.cls = &OBJC_CLASS_$_Teacher;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
(void *)&OBJC_CATEGORY_SETUP_$_Teacher_$_English,
};
//分类数组 编译器最后生成了一个数组,数组的元素就是我们创建的各个分类,用来在运行时加载分类。
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Teacher_$_English,
};
虽然分类可以声明属性,但是编译时,系统并没有生成分类属性的 get/set
方法,所以,这就是为什么分类要利用runtime
动态添加属性。
分类为什么不生成setter
和getter
每个类的内存布局在编译时期就已经确定了,运行时才加载的category
无法添加属性和实例变量。
使用objc_associate
系列函数绑定属性的时候这些变量存储在了哪里?
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// 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));
DisguisedPtr<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
object->setHasAssociatedObjects();
}
/* establish or replace the association */
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;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// release the old value (outside of the lock).
association.releaseHeldValue();
}
在runtime
中存在一个类型为AssociationHashMap
的哈希映射表保存着对象动态添加的属性,每个对象以自身地址为key
维护着一个绑定属性表,我们动态添加的属性就都存储在这个表里,这也是动态添加property
能成功的基础。