上篇文章讲到,实现了+ load方法
的类是非懒加载类,否则就是懒加载类。
+ load方法
是在main函数之前被调用的。这个时候为了能后保证+ load方法
能被调用,就必须提前把这个类加载好。_dyld_objc_notify_register()
-> read_image
-> realizeClassWithoutSwift)
-> methodizeClass
->attachLists
对rw赋值。那么懒记载类是如何加载的呢?
在我们第一次使用这个类的时候,也就是给这个类发送第一条消息的时候,懒加载的类才会被真正加载。
在之前的篇章中我们也讲到过消息发送,消息发送中有一个很重要的方法lookUpImpOrForward
。我们提到过
!cls->isRealized()
用来初始化懒加载类的。在Object-C
环境下,经过一系列的函数调用,会神奇的来到了我们上篇文章学习的realizeClassWithoutSwift
。
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) { ... if (!cls->isRealized()) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); // runtimeLock may have been dropped but is now locked again } ... } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) { return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) { return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); } static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked) { lock.assertLocked(); if (!cls->isSwiftStable_ButAllowLegacyForNow()) { // Non-Swift class. Realize it now with the lock still held. // fixme wrong in the future for objc subclasses of swift classes realizeClassWithoutSwift(cls); if (!leaveLocked) lock.unlock(); } else { // Swift class. We need to drop locks and call the Swift // runtime to initialize it. lock.unlock(); cls = realizeSwiftClass(cls); assert(cls->isRealized()); // callback must have provoked realization if (leaveLocked) lock.lock(); } return cls; } 复制代码
我们来测试下,懒加载类能不能满足!cls->isRealized()
条件
AKPerson没有实现 + load
方法,是懒加载类,主程序调用[AKPerson alloc]
的初始化方法。
结论一:懒加载类会在第一次调用的时候进行加载,加载的时机是在消息查找流程中的lookUpImpOrForward方法中。
父类AKPerson实现 + load
方法,子类AKStudnet不实现 + load
方法。清理缓存,主程序子类
调用初始化方法。
结论二:父类实现
+ load
, 子类不实现+ load
。父类是非懒加载类,子类是懒加载类。
父类AKPerson不实现 + load
方法,子类AKStudnet实现 + load
方法。清理缓存,主程序子类
先调用的初始化方法,父类
再调用的初始化方法。
发现父类没有进入!cls->isRealized()
, 父类是懒加载类。因为递归调用realizeClassWithoutSwift
完善继承链并处理当前类的父类、元类;如果有父类,就通过addSubclass
把当前类放到父类的子类列表中去
if (!cls) return nil; ... supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); metacls = realizeClassWithoutSwift(remapClass(cls->ISA())); ... // Update superclass and metaclass in case of remapping cls->superclass = supercls; cls->initClassIsa(metacls); ... // Connect this class to its superclass`s subclass lists if (supercls) { addSubclass(supercls, cls); } else { addRootClass(cls); } 复制代码
结论三:如果子类实现
+ load
,那么父类也会在子类被加载的时候,一起被加载。原因是子类在加载的时候会对父类和元类进行处理。
新建AKPerson + Test
分类
clang -rewrite-objc AKPerson+Test.m -o category.cpp
,打开cpp文件可以发现。
__DATA的__
的objc_catlist
中static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= { &_OBJC_$_CATEGORY_AKPerson_$_Test, }; 复制代码
static struct _category_t _OBJC_$_CATEGORY_AKPerson_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = { "AKPerson", 0, // &OBJC_CLASS_$_AKPerson, (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_AKPerson_$_Test, (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_AKPerson_$_Test, 0, 0, }; 复制代码
objc源码中搜索category_t
struct category_t { const char *name; // 类的名字,不是分类的名字 classref_t cls; // 类对象 struct method_list_t *instanceMethods; // 分类上存储的实例方法 struct method_list_t *classMethods; // 分类上存储的类方法 struct protocol_list_t *protocols; // 分类上所实现的协议 struct property_list_t *instanceProperties; // 分类所定义的实例属性 // Fields below this point are not always present on disk. struct property_list_t *_classProperties; // 分类所定义的类属性 method_list_t *methodsForMeta(bool isMeta) { if (isMeta) return classMethods; else return instanceMethods; } property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi); }; 复制代码
为什么分类要将实例方法和类方法分开保存呢?
类和元类加载过程中不断编译,实例方法存在类中,类方法存在元类中,已经确定好其方法归属的地方;而分类晚于类和元类的加载。
我们现在知道了类分为了懒加载类
和 非懒加载类
,它们的加载时机是不一样的,那么分类的加载又是怎么样的呢?
在分析前,还要搞清楚一点,分类必须依附于类而存在,如果只有分类,没有类,那么从逻辑上是说不通的,就算实现了,编译器也会忽略掉。
分类的加载在两处出现过:
_read_images
的Discover categories.
methodizeClass
。// Discover categories. // 发现和处理所有Category for (EACH_HEADER) { // 外部循环遍历找到当前类,查找类对应的Category数组 category_t **catlist = _getObjc2CategoryList(hi, &count); bool hasClassProperties = hi->info()->hasCategoryClassProperties(); for (i = 0; i < count; i++) { // 内部循环遍历当前类的所有Category category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); // 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。 bool classExists = NO; if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { // 将Category添加到对应Class的value中,value是Class对应的所有category数组 addUnattachedCategoryForClass(cat, cls, hi); // 将Category的method、protocol、property添加到Class if (cls->isRealized()) { remethodizeClass(cls); classExists = YES; } if (PrintConnecting) { _objc_inform("CLASS: found category -%s(%s) %s", cls->nameForLogging(), cat->name, classExists ? "on existing class" : ""); } } // 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作 // 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的 if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { addUnattachedCategoryForClass(cat, cls->ISA(), hi); if (cls->ISA()->isRealized()) { remethodizeClass(cls->ISA()); } if (PrintConnecting) { _objc_inform("CLASS: found category +%s(%s)", cls->nameForLogging(), cat->name); } } } } 复制代码
static void methodizeClass(Class cls) { ... // Attach categories. category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/); attachCategories(cls, cats, false /*don't flush caches*/); ... } 复制代码
为了方便定位,我们添加了一些调试代码
lookupOrForward
-> realizeClassWithoutSwift
开始加载内存methodizeClass
处理父类、元类关系unattachedCategoriesForClass
返回NULL通过两次的断点调试,我们发现懒加载的分类,在运行时期间没有进行添加分类的操作,我们来看看分类中的方法是否被添加进来。
dyld
-> _objc_init
-> map_images
-> _read_images
-> realizeClassWithoutSwift
->methodizeClass
加载类到内存中methodizeClass
处理父类、元类关系unattachedCategoriesForClas
返回NULL懒加载的分类不是运行时添加的,我们来看看分类中的方法是否被添加进来。
(lldb) p *$3 (class_rw_t) $4 = { flags = 2148139008 version = 7 ro = 0x00000001000011f0 methods = { list_array_tt<method_t, method_list_t> = { = { list = 0x0000000100001168 arrayAndFlag = 4294971752 } } } properties = { list_array_tt<property_t, property_list_t> = { = { list = 0x0000000000000000 arrayAndFlag = 0 } } } protocols = { list_array_tt<unsigned long, protocol_list_t> = { = { list = 0x0000000000000000 arrayAndFlag = 0 } } } firstSubclass = nil nextSiblingClass = 0x00007fff92d22080 demangledName = 0x0000000000000000 } 复制代码
(lldb) p $8.get(1) (method_t) $15 = { name = "load" types = 0x0000000100000f8c "v16@0:8" imp = 0x0000000100000c10 (objc-debug`+[AKPerson load] at AKPerson.m:12) } (lldb) p $8.get(2) (method_t) $16 = { name = "cate_instanceMethod" types = 0x0000000100000f8c "v16@0:8" imp = 0x0000000100000da0 (objc-debug`+[AKPerson(test) cate_instanceMethod] at AKPerson+test.m:34) } 复制代码
通过上述的lldb调试,我们发现,我们分类中的方法已经被添加到ro中了。
结论:不管是懒加载类或是非懒加载类,懒加载分类在编译时就确定了。
按照之前的理论,懒加载的类是在第一次发送消息的时候才会被加载的,函数调用栈应该是lookupImpOrForward
-> realizeClassMaybeSwiftAndLeaveLocked
-> realizeClassMaybeSwiftMaybeRelock
-> realizeClassWithoutSwift
-> methodizeClass
。我们测试下。
这一次通过 unattachedCategoriesForClass
取到值了,并且在这之前 cls 的 ro 中并没有分类的 initialize 方法:
但是我们的函数调用栈,不是发送消息的流程,而走的是 load_images
的 prepare_load_methods
方法呢?
read_images
-> addUnattachedCategoryForClass
prepare_load_methods
-> realizeClassWithoutSwift
-> unattachedCategoriesForClass
提前了实现类的信息/*********************************************************************** * load_images * Process +load in the given images which are being mapped in by dyld. * * Locking: write-locks runtimeLock and loadMethodLock **********************************************************************/ extern bool hasLoadMethods(const headerType *mhdr); extern void prepare_load_methods(const headerType *mhdr); 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); } // Call +load methods (without runtimeLock - re-entrant) call_load_methods(); } 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 if (cls->isSwiftStable()) { _objc_fatal("Swift class extensions and categories on Swift " "classes are not allowed to have +load methods"); } realizeClassWithoutSwift(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); } } 复制代码
_getObjc2NonlazyCategoryList
获取的所有的非懒加载分类,然后遍历这些非懒加载分类,加载这些分类所依赖的类。realizeClassWithoutSwift
方法来加载类结论:非懒加载分类让我们的懒加载类实现提前了,所以说懒加载类并不一定只会在第一次消息发送的时候加载,还要取决于有没有非懒加载的分类,如果有非懒加载的分类,那么就走的是
load_images
里面的prepare_load_methods
的realizeClassWithoutSwift
。
非懒加载类的流程我们十分熟悉了,在 _read_images 里面进行加载,而此时,分类也是非懒加载。
methodizeClass
处断点:unattachedCategoriesForClass
取出来的是 NULL,显然分类不是在这个地方被加载的
_read_images
的 Discover categories
处断点remethodizeClass
方法
/*********************************************************************** * remethodizeClass * Attach outstanding categories to an existing class. * Fixes up cls`s method list, protocol list, and property list. * Updates method caches for cls and its subclasses. * Locking: runtimeLock must be held by the caller **********************************************************************/ static void remethodizeClass(Class cls) { category_list *cats; bool isMeta; runtimeLock.assertLocked(); isMeta = cls->isMetaClass(); // Re-methodizing: check for more categories if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) { if (PrintConnecting) { _objc_inform("CLASS: attaching categories to class '%s' %s", cls->nameForLogging(), isMeta ? "(meta)" : ""); } attachCategories(cls, cats, true /*flush caches*/); free(cats); } } 复制代码
remethodizeClass
有一个 attachCategories
方法// Attach method lists and properties and protocols from categories to a class. // Assumes the categories in cats are all loaded and sorted by load order, // oldest categories first. static void attachCategories(Class cls, category_list *cats, bool flush_caches) { if (!cats) return; if (PrintReplacedMethods) printReplacements(cls, cats); bool isMeta = cls->isMetaClass(); // fixme rearrange to remove these intermediate allocations method_list_t **mlists = (method_list_t **) malloc(cats->count * sizeof(*mlists)); property_list_t **proplists = (property_list_t **) malloc(cats->count * sizeof(*proplists)); protocol_list_t **protolists = (protocol_list_t **) malloc(cats->count * sizeof(*protolists)); // Count backwards through cats to get newest categories first int mcount = 0; int propcount = 0; int protocount = 0; int i = cats->count; bool fromBundle = NO; while (i--) { auto& entry = cats->list[i]; method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { mlists[mcount++] = mlist; fromBundle |= entry.hi->isBundle(); } property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); if (proplist) { proplists[propcount++] = proplist; } protocol_list_t *protolist = entry.cat->protocols; if (protolist) { protolists[protocount++] = protolist; } } auto rw = cls->data(); prepareMethodLists(cls, mlists, mcount, NO, fromBundle); rw->methods.attachLists(mlists, mcount); free(mlists); if (flush_caches && mcount > 0) flushCaches(cls); rw->properties.attachLists(proplists, propcount); free(proplists); rw->protocols.attachLists(protolists, protocount); free(protolists); } 复制代码
注意英文注释:
attachCategories
与attachLists
原理基本一致 (参考类的加载):
attachLists
添加分类的方法、属性、协议memmove
将原数据移到末尾memcpy
把新数据拷贝到起始位置注:
attachCategories
这个方法只会在实现了 非懒加载分类
下才会被调用,而来到 attachCategories
之前又取决于类是否为懒加载,+ load方法
是在main函数之前被调用的。这个时候为了能后保证+ load方法
能被调用,就必须提前把这个类加载好。load
方法,编译时确定, 直接处理 data() - ro。load
方法,运行时确定。情景 | 类的加载 | 分类的加载 |
---|---|---|
懒加载分类 + 懒加载类 | 第一次发送 | 编译时 |
懒加载分类 + 非懒加载类 | _read_images | 编译时 |
非懒加载分类 + 懒加载类 | load_images(非懒加载分类让我们的懒加载类实现提前了) | 类加载之后的 methodizeClass |
非懒加载分类 + 非懒加载类 | _read_images | 类加载之后的 reMethodizeClass |
如果类有多个分类,方法调用顺序如何呢?
Person类
有AKPerson+Test1
和AKPerson+Test1
两个分类,三者都声明和实现类- sayHi
方法,主程序调用[[AKPerson alloc] sayHi]
;。
+ load
方法响应Compile Sources
最后一个分类
+ load
方法Compile Sources
最后一个分类
AKPerson+Test1
实现 + load
方法, AKPerson+Test2
不实现 + load
方法AKPerson+Test2
实现 + load
方法, AKPerson+Test1
不实现 + load
方法+ load
方法的分类。
结论1:
一般方法先调用分类,后调用主类。
结论2:
+load
方法,就响应Compile Sources
最后一个分类+load
方法,响应非懒加载分类。因为懒加载分类在编译时就已经加载到内存,而非懒加载分类运行时才加载懒加载类 + 非懒加载分类
情况下,分类加载到内存时会调用load_image
,那么我们在该种情况下进行探索.
在load_image
实现处打下断点,发现类和分类都没有打印+load
方法:load_image
先于+load
方法
注意英文注释:
prepare_load_methods
void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); // 1.获取非懒加载类列表 classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count); for (i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } // 2.获取非懒加载分类列表 category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count); for (i = 0; i < count; i++) { category_t *cat = categorylist[i]; Class cls = remapClass(cat->cls); const class_ro_t *ro = (const class_ro_t *)cls->data(); const char *cname = ro->name; const char *oname = "AKPerson"; if (cname && (strcmp(cname, oname) == 0)) { printf("_getObjc2NonlazyClassList 类名 :%s - %p 分类名: %s\n",cname,cls,cat->name); } 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); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); } } 复制代码
/*********************************************************************** * prepare_load_methods * Schedule +load for classes in this image, any un-+load-ed * superclasses in other images, and any categories in this image. **********************************************************************/ // Recursively schedule +load for cls and any un-+load-ed superclasses. // cls must already be connected. 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); } 复制代码
/*********************************************************************** * add_category_to_loadable_list * Category cat`s parent class exists and the category has been attached * to its class. Schedule this category for +load after its parent class * becomes connected and has its own +load method called. **********************************************************************/ void add_category_to_loadable_list(Category cat) { IMP method; loadMethodLock.assertLocked(); method = _category_getLoadMethod(cat); // Don`t bother if cat has no +load method if (!method) return; if (PrintLoading) { _objc_inform("LOAD: category '%s(%s)' scheduled for +load", _category_getClassName(cat), _category_getName(cat)); } 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++; } 复制代码
prepare_load_methods 分析:
_getObjc2NonlazyClassList
获取非懒加载类
列表schedule_class_load
遍历类列表+ load
方法,保证父类的 + load
方法顺序排列在子类前面add_class_to_loadable_list
把类的+load
方法存在loadable_classes
里面_getObjc2NonlazyCategoryList
获取非懒加载分类
列表realizeClassWithoutSwift
来防止类没有初始化(若已经初始化了则不影响)add_category_to_loadable_list
把分类的+load
方法到loadable_categories
现在我们知道+ load
在 load_images
里调用,到底怎么调用的呢?
/*********************************************************************** * call_load_methods * Call all pending class and category +load methods. * Class +load methods are called superclass-first. * Category +load methods are not called until after the parent class`s +load. * * This method must be RE-ENTRANT, because a +load could trigger * more image mapping. In addition, the superclass-first ordering * must be preserved in the face of re-entrant calls. Therefore, * only the OUTERMOST call of this function will do anything, and * that call will handle all loadable classes, even those generated * while it was running. * * The sequence below preserves +load ordering in the face of * image loading during a +load, and make sure that no * +load method is forgotten because it was added during * a +load call. * Sequence: * 1. Repeatedly call class +loads until there aren`t any more * 2. Call category +loads ONCE. * 3. Run more +loads if: * (a) there are more classes to load, OR * (b) there are some potential category +loads that have * still never been attempted. * Category +loads are only run once to ensure "parent class first" * ordering, even if a category +load triggers a new loadable class * and a new loadable category attached to that class. * * Locking: loadMethodLock must be held by the caller * All other locks must not be held. **********************************************************************/ 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) { 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; } 复制代码
static void call_class_loads(void) { ... // Call all +loads for the detached list. 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; if (PrintLoading) { _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging()); } (*load_method)(cls, SEL_load); } ... } 复制代码
static bool call_category_loads(void) { ... // 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); cats[i].cat = nil; } } ... return new_categories_added; } 复制代码
objc_autoreleasePoolPush
压栈一个自动释放池call_load_methods
, 发送消息调用类的+load
方法。call_category_loads
,循环发送消息调用分类的+load
方法。(*load_method)(cls, SEL_load);
调用+ load
的过程,就是objc_msgSend(cls, SEL_load)
的过程。objc_autoreleasePoolPop
出栈一个自动释放池+load
方法Initializes the class before it receives its first message.
在这个类接收第一条消息之前调用。当该类不使用时,该方法可能永远不会被调用。
在lookUpImpOrForward
-> initializeAndLeaveLocked
-> initializeAndMaybeRelock
-> initializeNonMetaClass
找到了它的踪迹。
/*********************************************************************** * class_initialize. Send the '+initialize' message on demand to any * uninitialized class. Force initialization of superclasses first. **********************************************************************/ void initializeNonMetaClass(Class cls) { ... // 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); } ... callInitialize(cls); ... } 复制代码
void callInitialize(Class cls) { ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize); asm(""); } 复制代码
isInitialized
,递归initializeNonMetaClass
父类(推测:调用顺序 先父类后子类)callInitialize
是一个普通的消息发送(推测:调用顺序 分类覆盖主类)AKPerson父类
和AKTeacher子类
都实现initialize
方法
父类
,后调用子类
子类
, 后调用父类
AKPerson + Test分类
和 AKTeacher + Test分类
都实现initialize
方法,主程序先后调用子类
和父类
初始化方法。
AKPerson父类
实现initialize
方法,AKTeacher子类
不实现initialize
方法,主程序调用子类
初始化方法。
initialize
走普通的消息发送机制。所以分类覆盖主类,当有多个分类都实现了initialize
方法,执行最后被加载到内存中的分类的方法。initialize
在类或者其子类的第一个方法被调用前(发送消息前)调用initialize
方法,在调用子类时,父类的initialize
方法调用过,则只调用子类的initialize
方法;父类的initialize
没用过,则先调用父类的initialize
方法,在调用子类的initialize
方法。(此时,再初始化父类的时候,不会再调用initialize方法)父类的initialize
方法本篇主要学习了 懒加载类
非懒加载类
懒加载分类
非懒加载分类
的加载; + load
和 + initialize
的调用。也是面试中百分比会被问到的地方,希望有所帮助。