整体比较
比较项目 | +load | +initialize |
---|---|---|
调用次数 | 1次 | 多次 |
是否需要显式调用父类实现 | 否 | 否 |
是否沿用父类的实现 | 否 | 是 |
调用顺序 | 父类->子类->分类 | 分类->父类->子类 |
调用时机 | 被添加到 runtime 时 | 收到第一条消息前,可能永远不调用 |
分类中的实现 | 类和分类都执行 | 覆盖类中的方法,只执行分类的实现 |
对方法执行顺序的一点见解
这两个方法的执行顺序和一般方法的执行顺序都有些差异,按照普通的方法调用顺序,如果方法funcA
在子类
和父类
中都实现了,在子类的实例对象中调用funcA
的话,会执行子类
的实现,如果是分类
中也实现了funcA
,那么优先执行分类
中的实现
但是 +load
方法和 +initialize
中父类和子类的循序调用都是先
调用父类
,后
调用子类
,通过源码得知,实际上是采用了递归
的方法,优先将父类指针传入,即起到优先调用父类中的实现,说通俗一点就是在方法的实现中,会先调用一遍父类的方法,这一点+load
方法和 +initialize
实现方式是一样
的
但是分类的方法调用顺序
二者就不一样
了,+load
方法将分类的实现放到了最后
,+initialize
是沿用了普通方法的调用,优先调用分类
中的实现,这一点我们不去细究,认为其实遵循了基本的调用顺序,那么+load方法为什么能最后调用分类中的方法呢?参看源码得知:实际上是优先遍历了父类和子类的方法之后,并且运用递归保证父类在子类之前调用,然后在全部调用完毕以后,再去遍历分类中的方法,进行分类调用
以上被划掉的解释不够严谨,甚至错误的,+initialize
方法调用底层是通过objc_sendMsg
即使用的消息转发机制
,所以分类的调用必然要要比原类先调用,但是+load
并非使用的是这种方式,其使用通过方法指针的方式直接调用,故不受消息转发机制
的限制,自定义调用顺序
源码摘录如下
打开 runtime 工程,我们接下来看看与 +load 方法相关的几个关键函数。首先是文件 objc-runtime-new.mm
中的 void prepare_load_methods(header_info *hi)
函数
void prepare_load_methods(header_info *hi)
{
size_t count, i;
rwlock_assert_writing(&runtimeLock);
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &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
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
然后跳入核心方法schedule_class_load
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
方法实现中自己调用了自己,并且传入了父类的指针
schedule_class_load(cls->superclass);
即起到优先调用父类的方法,然后打开文件 objc-loadmethod.m
,找到其中的 void call_load_methods(void)
函数。
void call_load_methods(void)
{
static BOOL loading = NO;
BOOL more_categories;
recursive_mutex_assert_locked(&loadMethodLock);
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
这里主要看do-while循环内部,do内部也有一个while循环,优先处理类中方法call_class_loads();
,在内层的while外边处理分类中的方法more_categories = call_category_loads();
然后在+initialize的源码中也有类似递归调用,保证父类执行的顺序的代码,但是对于分类中没有做特殊的处理
void _class_initialize(Class cls)
{
...
Class supercls;
BOOL reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
monitor_enter(&classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
monitor_exit(&classInitLock);
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
cls->nameForLogging());
}
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
...
}
主要是这里
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
由于最近几年一直接触的是Swift 的项目,对于OC的一些研究渐渐减少,造成一些概念的模糊,现在想重新拾起,对与文章中错误请各位大神指正
网友评论