上篇文章講到,實現了+ load方法
的類是非懶加載類,不然就是懶加載類。面試
+ load方法
是在main函數以前被調用的。這個時候爲了能後保證+ load方法
能被調用,就必須提早把這個類加載好。_dyld_objc_notify_register()
-> read_image
-> realizeClassWithoutSwift)
-> methodizeClass
->attachLists
對rw賦值。那麼懶記載類是如何加載的呢?swift
在咱們第一次使用這個類的時候,也就是給這個類發送第一條消息的時候,懶加載的類纔會被真正加載。數組
在以前的篇章中咱們也講到過消息發送,消息發送中有一個很重要的方法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()
條件bash
AKPerson沒有實現 + load
方法,是懶加載類,主程序調用[AKPerson alloc]
的初始化方法。app
結論一:懶加載類會在第一次調用的時候進行加載,加載的時機是在消息查找流程中的lookUpImpOrForward方法中。函數
父類AKPerson實現 + load
方法,子類AKStudnet不實現 + load
方法。清理緩存,主程序子類
調用初始化方法。post
結論二:父類實現
+ 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
的調用。也是面試中百分比會被問到的地方,但願有所幫助。