iOS initialize方法及与load的区别

  我们都知道initialize方法只有当类或子类第一次收到消息时才会调用。
我们在这个方法里打上断点来看看,比如我调用[Human new], 可以看到如下堆栈信息。

[Human new]

从源码里我们可以看到,在lookUpImpOrForward 里面,有初始化的判断。

 if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock may have been dropped but is now locked again

        // If sel == initialize, class_initialize will send +initialize and 
        // then the messenger will send +initialize again after this 
        // procedure finishes. Of course, if this is not being called 
        // from the messenger then it won't happen. 2778172
    }

一步步往下看 ,发现在 initializeNonMetaClass里 调用了callInitialize(cls);,而callInitialize实现如下

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}

可以看出在这里通过objc_msgSend的方式 对initialize进行了调用。

再来看看子类和父类同时实现initialize的情况

我这里Person类继承Human,都实现initialize,调用一些[Person new],按理来说应该只调用Personinitialize
实际结果如下:

[Person new]

发现先调用了父类的initialize,再调用了子类的initialize
initializeNonMetaClass源码里我们可以找到原因所在
// Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
这里会确保在开始初始化cls之前,super已完成初始化。所以这里会调用父类的initialize,往后面走再调用子类的initialize

如果在分类里实现initialize,那么分类的initialize则会‘‘覆盖’’本类的initialize,这种情况我们在这一篇中已经捋过了(以及load方法)。

那么最后总结一下+load+initialize区别
+load +initialize
调用时机 在类和分类加入Runtime的时候调用 类或子类第一次收到消息时
调用方式 直接通过函数指针调用((*load_method)(cls, @selector(load))) objc_msgSend
调用顺序 父类->子类->分类(分类顺序看文件编译顺序) 父类->子类(分类有实现,则父类->分类)
本类未实现,是否调用父类
存在分类时 全都调用,晚于本类的调用 ‘‘覆盖’’本类的方法,只调用分类的

end

你可能感兴趣的:(iOS initialize方法及与load的区别)