问自己两个问题:
1.应用启动在main
函数之前到底做了什么事情?
2.类、分类中load
方法的加载顺序怎样的?分类中出现的与主类同名的方法,会调用哪一个呢?
这些问题,不跟踪一次底层的源码?怎么会领悟得透彻呢?
我们实现一个类,然后在load
方法中打个断点看看:
这个调用栈的信息就非常丰富,从dyld-_dyld_start
开始,经历了一系列步骤,最终进入了load_images
,在load_images
方法中调用了load
方法。
我们到Apple Open Source网站下载一份最新的dyld
源码、system
源码、 dispatch
源码、objc
源码。(目前最新版本分别是dyld-852.2
,Libsystem-1292.120.1
,libdispatch-1271.120.2
,objc4-824
),然后就可以根据load
断点打印的调用栈来一点点跟。
一、dyld内部的主要流程
__dyld_start
开始于dyldStartup.s
汇编代码中:
这里标注了__dyld_start
调用了dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
开始初始化,最后调用了main
函数进入主程序。
所以重点就在dyldbootstrap::start
流程中了,由于步骤较多,我们就通过流程图来表示,具体如下:
简单来说:
main
之前通过,dyld
对主程序运行环境进行初始化,生成ImageLoader
把动态库生成的image
加载到内存中,然后进行链接绑定,接着初始化所有动态库,再接着初始化libSystem
,libdispatch
,libobjc
。其中libobjc
的初始化函数_objc_init
,初始化运行环境,并且通过_dyld_objc_notify_register(&map_images, load_images, unmap_image)
注册读取images,加载images的load函数。
在进入后续部分之前,我们需要先注意一下_objc_init函数:
-
environ_init()
:读取影响运行的环境变量 -
tls_init()
:关于线程key的绑定 -
static_init()
:运行c++静态构造函数。在dyld调用我们的静态构造函数之前,libdispatch会调用_objc_init() -
runtime_init()
:运行时环境初始化,主要是unattachedCategories、allocatedClasses -
exception_init()
:objc的异常处理系统 -
cache_t::init()
:缓存条件初始化 -
_imp_implementationWithBlock_init()
:启动回调机制 -
_dyld_objc_notify_register(&map_images, load_images, unmap_image)
:管理文件中和动态库中的符号,然后调用load方法。
二、map_images流程
该阶段主要是管理文件中和动态库中的所有符号,包括class
、protocol
、selector
、 category
。
2.1.map_images
主要用以处理dyld
给出的image
该函数直接进入了map_images_nolock
函数。接下来我们先看看这个函数主要作恶什么事情:
void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) {
if (firstTime) {
/*首次执行作一些必要的初始化*/
preopt_init();
}
/*通过image的地址,创建header_info信息,并且添加到hList中*/
/*mach_header对应每个动态库的macho*/
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
hList[hCount++] = hi;
if (firstTime) {
/*namedSelectors初始化*/
sel_init(selrefCount);
/*AutoreleasePoolPage::init();//自动释放池初始化
SideTablesMap.init();//散列表初始化
_objc_associations_init();//关联表初始化
*/
arr_init();
}
if (hCount > 0) {
/*准备工作完成后就会进入_read_images*/
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
}
2.2.接下来在看_read_images
的流程
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
/*先看一下这个宏定义,他是for循环的条件语句,每次取出hi = hList[hIndex],并且hIndex++。所以在下面会看到EACH_HEADER的写法*/
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
/*1.初始化TaggedPointer, 创建gdb_objc_realized_classes*/
if (!doneOnce) {
doneOnce = true;
if (DisableTaggedPointers) {
disableTaggedPointers();
}
initializeTaggedPointerObfuscator();
int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
/*2.Fix up @selector references*/
static size_t UnfixedSelectors;
{
for (EACH_HEADER) {
SEL *sels = _getObjc2SelectorRefs(hi, &count);
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
/*namedSelectors.get().insert(name)*/
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
/*3.Discover classes. Fix up unresolved future classes. Mark bundle classes*/
for(EACH_HEADER){
classref_t const *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[I];
/*readClass:旧类改动后会生成新的类,并重映射到新的类上*/
/*mangledName !=nil: NXMapInsert(nonMetaClasses(), cls->ISA(), cls) || NXMapInsert(gdb_objc_realized_classes, name, cls)*/
/*mangledName ==ni:类自身和元类allocatedClasses.insert(cls)*/
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
/*4.Fix up remapped classes*/
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[I]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[I]);
}
}
}
/*5.Fix up old objc_msgSend_fixup call sites*/
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
/*6.Discover protocols. Fix up protocol refs*/
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map, isPreoptimized, isBundle);
}
}
//Fix up @protocol references
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[I]);
}
}
/* 7.Discover categories. Only do this after the initial category attachment has been done*/
if (didInitialAttachCategories) {
for (EACH_HEADER) {
/*cls->isStubClass():objc::unattachedCategories.addForClass(lc, cls);*/
/*First, register the category with its target class. Then, rebuild the class's method lists (etc) if the class is realized*/
load_categories_nolock(hi);
}
}
/*8.Realize non-lazy classes (for +load methods and static instances)*/
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
addClassTableEntry(cls);
realizeClassWithoutSwift(cls, nil);
}
}
/*9.Realize newly-resolved future classes, in case CF manipulates them*/
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[I];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
}
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[I];
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.
continue;
}
// 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) {
/*实例方法/实例属性:class本身已经实现了,会将相关方法属性添加到class,否则添加到unattachedCategories*/
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
}else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)){
/*类方法/类属性:class-isa本身已经实现了,会将相关方法属性添加到metadata-class,否则添加到unattachedCategories*/
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
processCatlist(hi->catlist(&count));
processCatlist(hi->catlist2(&count));
}
三、load_images流程
该阶段主要是准备load方法,包括主类和分类中的,然后调用load方法。
入口函数,准备load方法,调用load方法
void load_images(const char *path __unused, const struct mach_header *mh){
prepare_load_methods((const headerType *)mh);// Discover +load methods
call_load_methods(); // Call +load methods (without classLock - re-entrant)
}
3.1.准备所有的load方法
包含load方法的class,category添加到对应的表中:
void prepare_load_methods(const headerType *mhdr){
size_t count, I;
runtimeLock.assertLocked();
/*superclsss->class:添加到loadable_classes表中,实体类型loadable_class*/
classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
/*category:添加到loadable_categories表中,实体类型loadable_category*/
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
add_category_to_loadable_list(categorylist[I]);
}
}
class添加到loadable_classes表中,加入的是loadable_class类型(包含cls和load-method, getLoadMethod就是查找load方法);分类添加到loadable_categories表中,加入的是loadable_category类型(包含category和load-method)
static void schedule_class_load(Class cls){
...
/*先插入superclass, 再插入class!!load方法的调用顺序*/
schedule_class_load(cls->getSuperclass());
add_class_to_loadable_list(cls);
...
}
/*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_classes_used作为计数器*/
void add_class_to_loadable_list(Class cls){
IMP method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
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++;
}
/*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_categories_used作为计数器*/
void add_category_to_loadable_list(Category cat){
IMP method = _category_getLoadMethod(cat);
if (!method) return; // Don't bother if cat has no +load method
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *) realloc(loadable_categories, loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
/* loadable_class结构体*/
struct loadable_class {
Class cls; // may be nil
IMP method;
};
/* loadable_category结构体*/
struct loadable_category {
Category cat; // may be nil
IMP method;
};
3.2.调用load方法
void call_load_methods(void){
...
do {
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);
...
}
//调用class-load方法,loadable_classes_used=0
static void call_class_loads(void){
...
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
(*load_method)(cls, @selector(load));
}
...
}
//调用category-load方法,loadable_categories_used=0
static bool call_category_loads(void){
...
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()) {
(*load_method)(cls, @selector(load));
cats[i].cat = nil;
}
}
...
}
非懒加载的类,也就是有load方法的类,回在main之前完成类的初始化,具体涉及到函数包括:
readClass
_getObjcNolazyClassList
realizeClassWithoutSwift
-
methodizeClass
懒加载的类,会在第一次消息的时候完成类的初始化: lookUpImpOrForward
realizeClassMaybeSwiftMaybeRelock
realizeClassWithoutSwift
-
methodizeClass
关于map_images和load_images中较为详细的流程可以看下图:
四、load、initialize方法加载流程实践
通过上面的分析我们知道了App
的启动流程中dyld
做了哪些事情,那么:
+load
和+initialize
分别是什么时候调用的呢?以及他们在父类,子类,分类中调用的顺序是什么样的呢?
我们先准备如下几个Class
和Category
:
Class-NXPerson
代码:
@interface NXPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)run;
@end
@implementation NXPerson
+ (void)load {NSLog(@"%s",__func__);}
+ (void)initialize {NSLog(@"%s",__func__);}
- (void)run{NSLog(@"%s",__func__);}
@end
Class-NXTeacher
代码:
@interface NXTeacher : NXPerson
- (void)teach;
@end
@implementation NXTeacher
+ (void)load {NSLog(@"%s",__func__);}
+ (void)initialize {NSLog(@"%s",__func__);}
- (void)teach{NSLog(@"%s",__func__);}
@end
Class-NXStudent
代码:
@interface NXStudent : NXPerson
- (void)study;
@end
@implementation NXStudent
+ (void)load{NSLog(@"%s",__func__);}
+ (void)initialize {NSLog(@"%s",__func__);}
- (void)study{NSLog(@"%s",__func__);}
@end
Category-NXPerson(Category)
代码:
@interface NXPerson(Category)
@end
@implementation NXPerson(Category)
+ (void)load{NSLog(@"%s",__func__);}
+ (void)initialize{NSLog(@"%s",__func__);}
- (void)run {NSLog(@"%s",__func__);}
@end
Category-NXTeacher(Category)
代码:
@interface NXTeacher(Category)
@end
@implementation NXTeacher(Category)
+ (void)load {NSLog(@"%s",__func__);}
+ (void)initialize{NSLog(@"%s",__func__);}
- (void)teach {NSLog(@"%s",__func__);}
@end
Category-NXStudent(Category)
代码:
@interface NXStudent(Category)
@end
@implementation NXStudent(Category)
+ (void)load {NSLog(@"%s",__func__);}
+ (void)initialize {NSLog(@"%s",__func__);}
- (void)study {NSLog(@"%s",__func__);}
@end
代码如上,一共三个类
NXPerson
、NXTeacher
、NXStudent
,后2者继承于前者;三个分类NXPerson(Category)
、NXTeacher(Category)
、NXStudent(Category)
。每个主类分别实现了-run
、-teach
、-study
和+load
、+initialize
方法。每个分类中也实现了主类的方法和+load
、+initialize
方法。
4.1.+load
方法
首先我们在没有任何调用的情况下的运行代码:
探索:
1.Class
和Category
的+load
方法都被调用了,+initialize
方法没有被调用。
2.Class
是不是一定在Category
前面打印呢?我们将NXPerson(Category)
放在NXStudent
和NXTeacher
之前,继续运行代码发现Class
的+load
依旧比Category
的+load
更先调用。
3.我们更换几个Class
的顺序,发现NXPerson
的+load
永远在最前,同级子类加载的顺序跟代码的顺序有关。至此我们可以推断父类的+load
先于子类调用。
4.我们更换几个Category
的顺序,发现谁的代码在前谁先打印,即分类的+load
方法加载的顺序是根据代码读取的顺序来决定的。
以上结论其实是可以在objc源码中找到答案的:在call_load_methods
中我们也可以看到call_class_loads
先于call_category_loads
调用(源码如下),这个决定了所有的父类的+load
更先读取。
void call_load_methods(void){
...
//调用loadable_classes中`loadable_class`绑定的`load`方法
call_class_loads();
//调用loadable_categories中`loadable_category`绑定的`load`方法
call_category_loads();
....
}
4.2.initialize方法
我们添加如下代码到main
中:
NXTeacher *p = [[NXTeacher alloc] init];
打印结果如下:
[NXPerson(Category) initialize]
[NXTeacher(Category) initialize]
反复运行打印的只有分类的+initialize
,以及分类对应类的父类的分类的+initialize
,并且父类先于之类调用.
探索:
1.+initialize
方法默认会在main之后,第一次访问该类的时候调用。通常首次访问的方法+alloc
、+self
、+class
、+new
等等类方法。
2.initialize
会先调用父类,再调用子类。如果同级有分类,则分类的方法会覆盖掉主类的方法,多个分类的会调用最后一个加载进内存的分类的方法。
上面我们讲到最先访问到一个类的方式通常是+alloc
、+self
、+class
、+new
方法,接下来我们就初始化一个[NXTeacher alloc]
,而Objective-C
的方法调用,底层都是通过消息转发objc_msgSend
实现的,那么[NXTeacher alloc]
会被转化为objc_msgSend(NXTeacher, @selector(alloc))
来进入汇编代码实现的快速查找流程从缓存中查找,那么第一次调用缓存中肯定没有alloc
方法的;缓存中没有找到,则会进入慢速查找流程lookUpImpOrForward
:
NEVER_INLINE IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){
...
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
...
}
static Class realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize){
if (slowpath(!cls->isRealized())) {
/*实现该类*/
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
if (slowpath(initialize && !cls->isInitialized())) {
/*初始化该类*/
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
return cls;
}
4.2.1.分类方法是怎么添加的?
static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) {
return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}
static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked){
...
if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
realizeClassWithoutSwift(cls, nil);
}
else {
cls = realizeSwiftClass(cls);
}
return cls;
}
static Class realizeClassWithoutSwift(Class cls, Class previously){
...
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
...
// Update superclass and metaclass in case of remapping
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
...
// Attach categories
methodizeClass(cls, previously);
return cls;
}
static void methodizeClass(Class cls, Class previously){
...
// Attach categories
if (previously) {
if (isMeta) {
//类方法添加到元类上
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
//实例方法添加到类上
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
void attachToClass(Class cls, Class previously, int flags){
...
auto &map = get();
auto it = map.find(previously);
if (it != map.end()) {
category_list &list = it->second;
if (flags & ATTACH_CLASS_AND_METACLASS) {
int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
}
else {
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags){
...
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[I];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
...//property_list_t, protocol_list_t
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
NO, fromBundle, __func__);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
...
}
...
}
//这个方法里边在插入的时候
void attachLists(List* const * addedLists, uint32_t addedCount) {
...
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
newArray->count = newCount;
array()->count = newCount;
/*第1个for循环相当于将老数据往后平移了addedCount个位置*/
for (int i = oldCount - 1; i >= 0; I--)
newArray->lists[i + addedCount] = array()->lists[I];
/*第2个for循环相当于将新数据按顺序放在前面*/
for (unsigned i = 0; i < addedCount; I++)
newArray->lists[i] = addedLists[I];
free(array());
setArray(newArray);
validate();
...
}
如此以来按顺序查找的时候,那么排在前面的category
方法会被优先调用,categoey-initialze
会被调用,而主类没有被调用的真正原因。
4.2.2.initialize
调用顺序
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
static Class initializeAndMaybeRelock(Class cls, id inst, mutex_t& lock, bool leaveLocked){
...
initializeNonMetaClass(nonmeta);
...
}
void initializeNonMetaClass(Class cls){
...
/*重点!!!,这里会先调用父类的initialize方法*/
supercls = cls->getSuperclass();
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
allInitialize(cls);
}
void callInitialize(Class cls){
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
}
通过以上的流程,我们可以看出initialize
方法的调用时先调用父类,再调用当前类的。
总结:
关于load
方法:
1.class
和category
的+load
方法都会被调用,是在main
之前被调用。
2.调用顺序先主类的+load
,再调用分类的+load
。主类的调用顺序为先superclass
,再subclass
。分类的+load
是根据代码读取的顺序。子类的+load
可能会在前面。
关于initialize
方法:
1.如果有分类,那么分类的方法列表会加载进主类的方法列表中,并且放在方法列表的头部,按照代码的读取顺序,最后读取到分类方法会放在最前面。按照顺序查找则最后加载进去的方法会被优先查找到(这也就是主类的同名方法会被分类【覆盖掉】的原因)。分类initialize
会被调用,而主类的initialize
不会被调用。
2.调用顺序依然是先superclass
,再subclass
。