「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。
关于Category 的使用,大家肯定都手到擒来了。
今天就来盘它Category。
面试的时候可能被问到
类的load方法中,能调用分类的方法吗?category如何被加载的?+load 方法调用顺序?
基于objc4-779.1源码,debug来看一看Category。
首先第一个问题类的load方法中,能调用分类的方法吗?,答案是肯定的,可以
- 通过
runtime的入口函数可以知道先加载完分类,后调用的load方法
我们通过捋清楚Category的加载来一步步解答。
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();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
忽略掉一堆 init ,重点来看 _dyld_objc_notify_register(&map_images, load_images, unmap_image);
这个方法会注册3个事件并给出回调。
重点来看一下map_images和load_images;
从这俩个回调方法里看,你会发现Category在map_images会加载完毕,而load_images会调用+load方法。这就解释了第一个问题:类的load方法中,能调用分类的方法。
主要看点:map_images
从map_images里来看看Category的加载。map_images最终会调用_read_images,这里就是核心部分了,从中间我们只看处理分类及类的部分
// Discover categories.
for (EACH_HEADER) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
auto processCatlist = [&](category_t * const *catlist) {
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
//printf("%s\n",cat->name);
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Ignore the category.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category ???(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
if (strcmp(cat->name, "HJJJJJ")==0) {
}
// 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));
}
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
//printf("11-%s\n",object_getClassName(cls));
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
const char *tempChar;
tempChar = object_getClassName(cls);
if (strcmp(tempChar, "Person")==0) {
}
realizeClassWithoutSwift(cls, nil);
}
}
通过断点会发现 分类部分的代码 objc::unattachedCategories.addForClass(lc, cls);,只是将class和其对应的category做了一个映射,插入到map中。真正要看的还是realizeClassWithoutSwift 这个方法。