问题: 简述一下load的调用时机以及调用顺序.
注: 本问题回答基于 objc4-818.2 版本.
load
方法是runtime在加载类、分类时自动调用的方法.
关于 load 方法的调用顺序主要有以下三点.
- 两个类没有继承关系或者是 所有的Category,那么load的调用顺序与
Compile Sources
中得编译顺序一致. 其可以通过prepare_load_methods
方法可知.
void prepare_load_methods (const headerType *mhdr) {
size_t count, i;
runtimeLock.assertLocked();
// 这一步获取顺序由 `Compile Source`的编译顺序确定.
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 内部会对父类的load方法进行添加加载.
schedule_class_load(remapClass(classlist[i]));
}
// 这一步获取顺序由 `Compile Source`的编译顺序确定.
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
- 两个类如果存在继承关系,那么首先会调用父类的load方法,这可以通过
schedule_class_load
相关源码可知.
static schedule_class_load(Class cls)
{
if (cls->info & CLS_LOADED) return;
// 如果存在superclass指针,那么会先添加父类的load方法.
if (cls->superclass) schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->info |= CLS_LOADED;
}
- load方法调用顺序是先调用类的load方法, 再调用Category的load方法.这一点可以通过查看源码中的
call_load_methods
方法来确认.
void call_load_methods(void) {
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 调用类的 load 方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 调用分类的load方法
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
load方法的调用方式是直接通过指针寻找,并非常规的方法调用方式(消息发送机制).所以不存在方法覆盖问题.这一点可以通过源码中 call_class_loads
、 call_category_loads
这两个方法进行确认.
static void call_class_loads(void) {
int i;
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
for (i = 0; i < used; i++) {
Class cls = classes[i];
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, @selector(load));
}
if (classes) free(classes);
}
static void call_category_loads(void) {
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, @selector(load));
cats[i].cat = nil;
}
}
// Compact detached list (order-preserving)
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[i];
} else {
shift++;
}
}
used -= shift;
// Copy any new +load candidates from the new list to the detached list.
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[i];
}
// Destroy the new list.
if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list.
// But if there's nothing left to load, destroy the list.
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
if (PrintLoading) {
if (loadable_categories_used != 0) {
_objc_inform("LOAD: %d categories still waiting for +load\n",
loadable_categories_used);
}
}
return new_categories_added;
}
问题: 简述一下 initialize 的调用时机以及内部原理.
initialize
调用时机是类第一次调用方法时调用的方法.
initialize
调用方式是 objc_msgSend,所以方法调用顺序以及调用过程按照 objc_msgSend 的方式来即可.
initialize
的调用顺序是先调用父类的 initialize
再调用自身的 initialize
. 调用顺序可以查看源码中得 initializeNonMetaClass
方法.
核心代码如下所示.
void initializeNonMetaClass(Class cls) {
....
Class supercls;
bool reallyInitialize = NO;
supercls = cls.getSuperclass();
if (supercls && !supercls->isInitialized()) {
// 如果父类存在,并且没有没有调用过initialize,那么先调用父类的方法.
initializeNonMetaClass(supercls);
}
...
}
Comments | 0 条评论