Runtime - Category
2021-07-26 本文已影响0人
ienos
struct category_t {
// 名字
const char *name;
// class
classref_t cls;
// 实例方法,cls 为 class
struct method_list_t *instanceMethods;
// 类方法,cls 为 meta
struct method_list_t *classMethods;
// 协议
struct protocol_list_t *protocols;
// 实例属性,cls 为 class
struct property_list_t *instanceProperties;
};
- category 支持协议
思考
- Category 什么时候加载属性和方法?
- Category 怎么加载属性和方法?
- Category 可以加载成员变量吗?
- Category 和 Class 的关系如何表示?
- Category 重载 Class 中的方法?
源码阅读
![](https://img.haomeiwen.com/i1929662/e63733c0abf002bb.png)
1. addUnattachedCategories: map class & category
/* objc-runtime-new.mm */
void _read_images(header_info **hList, uint32_t hCount)
{
...
category_t **catlist = _getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
...
// Process this category.
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
...
}
}
...
}
-
_read_images
中先调用_getObjc2CategoryList
获取category_list
- 再遍历
category_list
调用addUnattachedCategoryForClass
关联 class 和 category
static void addUnattachedCategoryForClass(category_t *cat, Class cls,
header_info *catHeader)
{
runtimeLock.assertWriting();
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);
}
NXMAPTable
保存一个 Key 为 Class,Value 为 category_list
2. unattachedCategory 中的 unattached 是什么意思? 什么时候 attached ?
static Class remethodizeClass(Class cls)
{
...
// 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);
}
}
remethodizeClass
在 _read_images
中有调用,说明需要在 class realized
后才能调用 attachedCategory
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
3. attachedCategory: category 中的方法、属性、协议添加到 class 中
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));
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[i];
/// method_list
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
/// property_list
property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
if (proplist) {
proplists[propcount++] = proplist;
}
/// protocol_list
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// rw->methods 添加 method_list
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// rw->properties 添加 property_list
rw->properties.attachLists(proplists, propcount);
free(proplists);
// rw->protocols 添加 protocols_list
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
-
prepareMethodLists
方法待研究?
4. category 重载 class 方法
例如重载 Company 中的 setValue:forKey
var count: UInt32 = 0
guard let methods = class_copyMethodList(Company.self, &count) else { return }
for index in 0..<count {
let sel = method_getName(methods[Int(index)])
print(String.fromCString(cs: sel_getName(sel), length: 30))
}
结果发现有两个同名的 SEL,setValue:forKey
![](https://img.haomeiwen.com/i1929662/594ffede341b087c.png)