demo项目的目录结构
上代码:
main.m (啥都没写):
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
return 0;
}
Person类:
@interface Person : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person.h"
@implementation Person
+ (void)load {
NSLog(@"Person load");
}
+ (void)initialize
{
NSLog(@"Person initialize()");
}
@end
Person+Eat类(即Person类的分类):
#import "Person.h"
@interface Person (Eat)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person+Eat.h"
@implementation Person (Eat)
+ (void)load {
NSLog(@"Person(Eat) load");
}
+ (void)initialize
{
NSLog(@"Person(Eat) initialize()");
}
@end
Person+Test类(即Person类的分类):
#import "Person.h"
@interface Person (Test)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Person+Test.h"
@implementation Person (Test)
+ (void)load {
NSLog(@"Person(Test) load");
}
+ (void)initialize
{
NSLog(@"Person (Test) initialize()");
}
@end
Student类:
#import "Person.h"
@interface Student : Person
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Student.h"
@implementation Student
+ (void)load {
NSLog(@"Student load");
}
+ (void)initialize
{
NSLog(@"Student initialize()");
}
@end
Student+Test1类(即Student类的分类):
#import "Student.h"
@interface Student (Test1)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Student+Test1.h"
@implementation Student (Test1)
+ (void)load {
NSLog(@"Student(Test1) load");
}
+ (void)initialize
{
NSLog(@"Student (Test1) initialize()");
}
@end
Student+Test类(即Student类的分类):
#import "Person.h"
@interface Student (Test2)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Student+Test2.h"
@implementation Student (Test2)
+ (void)load {
NSLog(@"Student(Test2) load");
}
+ (void)initialize
{
NSLog(@"Student (Test2) initialize()");
}
@end
Cat类:
@interface Cat : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Cat.h"
@implementation Cat
+ (void)load {
NSLog(@"Cat load");
}
+ (void)initialize
{
NSLog(@"Cat initialize()");
}
@end
Dog类:
@interface Dog : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Dog.h"
@implementation Dog
+ (void)load {
NSLog(@"Dog load");
}
+ (void)initialize
{
NSLog(@"Dog initialize()");
}
@end
总结一下上面代码的继承和分类的关系:
运行结果与分析
从代码和打印结果可以看出,即使main.m文件没有导入Person或者Student等类或者分类的头文件,这些类的load()都会被调用,说明程序开始运行时,代码被加载进内存后,所以类和分类的load()就会被执行。当这些load()执行完成后,main()函数才开始被执行。注意:demo执行完了,所有类的initialize()都不会被调用。因为在该demo的运行过程中,没有类接收到消息。一个类接收到消息指的是在代码里面调用 [类名 方法名];
或者调用 objc_msgSend(类对象或者实例对象,方法的SEL);
。
调整类和分类的编译顺序,打印结果如下:
在上面的demo代码的基础上,往main.m文件的添加一些头文件和一个方法调用
此时的main.m文件的代码如下:
#import <Foundation/Foundation.h>
#import "Dog.h" //添加的内容
#import "Student.h" //添加的内容
int main(int argc, const char * argv[]) {
@autoreleasepool { //添加的内容
[Student alloc]; //添加的内容
} //添加的内容
return 0;
}
运行结果和分析
[Student alloc];
表示向Student类发送alloc消息。当一个类在第一次接收到消息时,该类的initialize方法就会被调用。此后,当该类第二次或第二次以上接收到消息时,该类的initialize方法就不再会被调用。
上一张图的编译顺序如下图:
load方法调用的源码解析:
先看load方法加载的整体的时序图
objc-os.mm文件
void _objc_init(void) //程序刚开始运行时被调用(该函数在main函数被调用之前被调用)
{
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();
lock_init();
exception_init();
//map_images和load_images都是一个函数。当_dyld_objc_notify_register函数被调用时,map_images函数会先被调用,然后才调用load_images函数。
//map_images函数用于构造所有的类实例及其元类实例 以及 这些类实例及其元类实例的方法列表、协议列表、属性列表和成员变量列表等。即map_images函数调用过后,一个程序的每一个类实例及其元类实例的方法列表、协议列表、属性列表和成员变量列表等都已经有该类(包括其对应的分类)的所有方法、所有协议、所有属性和成员变量了。
//load_images函数内部会先调用每一个类的load函数。等到所有的类的load函数被调用完成后,所有类的分类的load函数才会被调用。所有类的load函数的调用顺序是根据编译顺序来决定,先编译的类的load函数就会先被调用。同理,先被编译的分类的load函数也会先被调用。
//注意:当一个类B继承父类A时,虽然类B在父类A之前被编译,但是因为是父子关系,所以父类A的load函数先被调用完之后,然后类B的load函数才会被调用。
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
上一篇博客已经分析了map_images函数里面的调用过程,所以现在分析load_images函数里面的调用过程。
objc-runtime-new.mm文件
/*
struct loadable_class { //结构体
Class cls; // may be nil,类对象
IMP method; //类对象对应的函数指针
};
static struct loadable_class *loadable_classes = nil; //loadable_class数组
*/
//程序刚开始运行时,该函数会在main函数调用之前被调用
void load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh); //把每个类都封装成loadable_class结构体,然后将这些loadable_class结构体实例放到同一个数组中。
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods(); //调用所有类及其分类的load函数
}
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i])); //该函数是个递归函数
}
category_t **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
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
/*
struct loadable_class { //结构体
Class cls; // may be nil,类对象
IMP method; //类对象对应的函数指针
};
static struct loadable_class *loadable_classes = nil; //loadable_class数组
*/
//该函数是个递归函数。该函数的作用是先把父类作为参数传入到schedule_class_load函数中,然后再把子类作为参数传入到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; //如果父类已经被添加过了(添加到loadable_classes数组中了),则直接返回而不会被重复添加了(不会再次被添加到loadable_classes数组中)
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
/*
struct loadable_class { //结构体
Class cls; // may be nil,类对象
IMP method; //类对象对应的函数指针
};
*/
//把一个类封装成loadable_class结构体,再将该结构体添加到loadable_classes数组中。该数组会在后面的函数中被读取,所以先记住loadable_classes数组是在此时被初始化的。
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
objc-loadmethod.mm
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// 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) { //loadable_classes_used是在上面的add_class_to_loadable_list函数中被赋值,该变量表示loadable_classes数组的长度
call_class_loads(); //1、先调用所有的类的load函数(根据编译顺序调用,有继承关系的例外,即如果有继承关系,则父类的load函数先被调用完成,子类的load函数才会被调用)
}
// 2. Call category +loads ONCE
more_categories = call_category_loads(); //2、然后才调用所有的分类的load函数(根据编译顺序调用)
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
/*
typedef void(*load_method_t)(id, SEL); //load_method_t是一个函数指针
struct loadable_class { //结构体
Class cls; // may be nil,类对象
IMP method; //类对象对应的函数指针,具体指的是load函数的指针
};
*/
//调用类的load方法,具体是通过函数指针直接调用load函数,而不是通过消息发送与接收流程来调用每一个类的load方法
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) { //遍历前面已经初始化号的loadable_classes数组,该数组元素类型是loadable_class结构体类型,该结构体包含了一个类的实例对象的指针及其load函数的指针
Class cls = classes[i].cls;
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, SEL_load); //通过函数指针直接调用load函数,所以不是通过消息发送与接收流程来调用每一个类的load方法。
}
// Destroy the detached list.
if (classes) free(classes);
}
//调用分类的load方法,也是通过函数指针直接调用load函数,而不是通过消息发送与接收流程来调用每一个分类的load方法。
static bool 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, SEL_load); //通过函数指针直接调用load函数,所以不是通过消息发送与接收流程来调用每一个类的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方法调用的源码分析
先看load方法加载的整体的时序图
[Student alloc];
等价于objc_msgSend([Student class], @selector(alloc) );
,表示向Student类发送alloc消息。initialize方法是在objc_msgSend()中被调用的。但objc_msgSend()是由汇编实现的。下图是从runtime源码工程里面搜索的,发现搜出来的objc_msgSend()都是由汇编实现。那方法调用既然是通过objc_msgSend()来实现向对象发送消息,那肯定会涉及到对被调用方法的查找过程,即从类对象或者元类对象的方法列表里面查找目标方法。而查找目标方法则是通过调用class_getInstanceMethod()
或者class_getClassMethod()
函数来实现。
class_getClassMethod()
的源码调用流程如下:
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel); //调用class_getInstanceMethod(),第1个参数是类的元类对象
}
class_getClassMethod()
内部是调用class_getInstanceMethod()
,所以只需要分析class_getInstanceMethod()
的源码,该函数的源码如下:
Method class_getInstanceMethod(Class cls, SEL sel) //sel指的是你要调用的方法
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrNil(cls, sel, nil,
NO/*initialize*/, NO/*cache*/, YES/*resolver*/); //查找所调用的实例或者类方法
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
/***********************************************************************
* lookUpImpOrNil.
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
**********************************************************************/
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver); //查找所调用的实例或者类方法
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
/***********************************************************************
* lookUpImpOrForward.
* The standard IMP lookup.
* initialize==NO tries to avoid +initialize (but sometimes fails)
* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
* Most callers should use initialize==YES and cache==YES.
* inst is an instance of cls or a subclass thereof, or nil if none is known.
* If cls is an un-initialized metaclass then a non-nil inst is faster.
* May return _objc_msgForward_impcache. IMPs destined for external use
* must be converted to _objc_msgForward or _objc_msgForward_stret.
* If you don't want forwarding at all, use lookUpImpOrNil() instead.
**********************************************************************/
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
if (initialize && !cls->isInitialized()) { //如果类对象没有初始化(即没有调用过该类或者其分类的initialize方法)
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst)); //调用类的initialize方法
runtimeLock.lock();
// 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
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
//递归函数,如果父类的initialize方法没有被调用,则先调用父类的,然后才调用子类的initialize方法。
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
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()) { //如果父类存在并且父类的initialize方法没有被调用过
_class_initialize(supercls); //递归调用自己
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
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);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
// 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: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
// bootstrapping problem of this versus CF's call to
// objc_exception_set_functions().
#if __OBJC2__
@try
#endif
{
callInitialize(cls); //调用当前类的initialize方法
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
//调用当前类的initialize方法,里面是通过objc_msgSend()函数调用的,但该函数是汇编实现的。
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
load方法和initialize方法的相同点:
- 都是类方法;
- 都是系统默认调用;
- 父类的load先被调用,子类的load才被调用。同理,父类的initialize先被调用,子类的initialize才被调用。
load方法和initialize方法的区别:
-
调用方式:load方式是通过函数指针直接被调用。而initialize方法则是通过消息发送机制被调用;
-
调用时机和覆盖问题:所有类及其分类的load方法都是在程序刚开始运行时并且在main函数执行之前就会被调用,不存在被分类的load方法的覆盖问题,即load方法的调用顺序是 父类->子类->分类。而initialize方法是在方法所属的类在第一次接收到消息时才会被调用,类第一次接收到消息指的是你第一次通过
[类名 方法名]
或者objc_msgSend(类对象, SEL)
来向类发送消息,类的initialize方法会被其分类的initialize方法覆盖(如果类的分类也实现了initialize方法的话)。 -
父类的调用次数:父类的load方法只能被调用一次。但是父类的initialize方法可能被调用多次(前提是子类的initialize方法没有被实现)
举例:
①假如B类和C类都是A类的子类,且CategoryA是A的分类,A、B、C和CategoryA都实现了initialize方法,则当你调用[B alloc]; [C alloc]
时,先分析[B alloc]
调用之后会发生:CategoryA的initialize方法会先被调用(A的initialize方法不会被调用,即A的initialize方法被CategoryA分类覆盖了,因为CategoryA的initialize方法是在A类的元类的方法列表里面的排在A类的initialize方法前面),然后调用B的initialize方法,最后[C alloc]
才会被调用,此时才会调用C类的initialize方法。
②假如B类和C类都是A类的子类,且CategoryA是A的分类,只有A和CategoryA实现了initialize方法,则当你调用[B alloc]; [C alloc]
时,先分析[B alloc]
调用之后会发生:CategoryA的initialize方法会先被调用(A的initialize方法不会被调用,即A的initialize方法被CategoryA分类覆盖了,因为CategoryA的initialize方法是在A类的元类的方法列表里面的排在A类的initialize方法前面),此时Runtime在B的元类找不到initialize方法,所以Runtime会通过B的元类的super指针找到A的元类,然后在A的元类的方法列表里面找到了CategoryA实现的initialize方法(在A的元类的方法列表里面,CategoryA的initialize方法排在A的initialize方法前面),然后调用CategoryA实现的initialize方法,最后[C alloc]
才会被调用,同理,此时Runtime在C的元类找不到initialize方法,所以Runtime会通过C的元类的super指针找到A的元类,然后在A的元类的方法列表里面找到了CategoryA实现的initialize方法,然后调用CategoryA实现的initialize方法。所以,在这个例子中,当你调用[B alloc]; [C alloc]
时,即先想B发送了alloc消息,然后想C发送了alloc消息,Runtime最终调用3次了CategoryA的initialize方法