打個符號斷點:_objc_init
,可以看到以下調(diào)用棧
1. dyld 鏈接動態(tài)庫
app在被打開的時候所依賴的動態(tài)庫會被加載到程序中.
dyld(the dynamic link editor)
這樣一部手機(jī)所有app都共用系統(tǒng)的動態(tài)庫,這樣做大大的減小了可執(zhí)行文件(ipa)大小.
將模擬器路徑上的PGRuntimeTrain.app ShowInFinder,然后:
otool -L PGRuntimeTrain.app/PGRuntimeTrain
PGRuntimeTrain.app/PGRuntimeTrain:
/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1349.54.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 1238.50.2)
/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1349.55.0)
/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 3600.7.47)
Foundation.framework
CoreFoundation.framework
UIKit.framework
libobjc.A.dylib
├──objc
└──runtime
libSystem.dylib
├── libdispatch//GCD
├── libsystem_c//C語言庫
├── libsystem_blocks//Block
└── libcommonCrypto//加密庫剔猿,比如常用的 md5 函數(shù)
在做一些"逆"的工作時在這個環(huán)節(jié)可以加入自己寫的.dylib
,功力不夠就不細(xì)說了
2. ImageLoader 加載鏡像文件
動態(tài)庫加載完成后就該加載我們自己的編寫的代碼編譯成的二進(jìn)制文件了,就是ImageLoaderXXXXXX
系列方法.這些image內(nèi)就編譯著我們自己寫的符號、代碼等.
3. _objc_init runtime初始化
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
//讀取鏡像前,基本環(huán)境的初始化
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}
3.1 map_2_images
map_2_images
└─map_images_nolock
└─_read_images
- map_images_nolock
map_images_nolock內(nèi)的已經(jīng)完成了:
header_info *hList[mhCount];==>類信息讀取到header_info
的鏈表數(shù)組
preopt_init==>優(yōu)化共享緩存的初始化
sel_init==>初始化方法列表
arr_init==>初始化自動釋放池+散列表
- _read_image
到_read_image
方法內(nèi)所有類的信息全放在header_info
的鏈表數(shù)組之中,以該鏈表數(shù)組出發(fā):
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
"IMAGE TIMES: first time tasks”—>support indexed_isa
"IMAGE TIMES: discover classes”—>_getObjc2ClassList
"IMAGE TIMES: remap classes”—>_getObjc2ClassRefs
"IMAGE TIMES: fix up selector references”—>_getObjc2SelectorRefs
"IMAGE TIMES: fix up objc_msgSend_fixup”—>_getObjc2MessageRefs
"IMAGE TIMES: discover protocols”—>_getObjc2ProtocolList
"IMAGE TIMES: fix up @protocol references”—>_getObjc2ProtocolRefs
"IMAGE TIMES: realize non-lazy classes”—>_getObjc2NonlazyClassList
"IMAGE TIMES: realize future classes”—>resolvedFutureClasses
"IMAGE TIMES: discover categories”—>_getObjc2CategoryList
1.確認(rèn)是否支持indexed_isa
2.讀取所有類信息
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
當(dāng)調(diào)用readClass
方法返回的Class newCls
與傳入的cls
不一樣時,則會將Class newCls
加了數(shù)組Class *resolvedFutureClasses
,方便后面對類做實現(xiàn)(第6步).
3.讀取所有方法信息
4.讀取所有協(xié)議信息
5.實現(xiàn)所有的
non-lazy classes
注意第2步:讀取所有類信息內(nèi)用的是:
classref_t *classlist = _getObjc2ClassList(hi, &count);
而現(xiàn)在用的是:
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
實現(xiàn)了+load
的類就是non-lazy classes
,沒有實現(xiàn)+load
的類就是lazy classes
realizeClass//cls->ro到cls->rw->ro
└─methodizeClass//向cls->rw內(nèi)填充方法列表,屬性列表,協(xié)議列表
└─attachCategories//讀取類對應(yīng)分類的內(nèi)容
5.1 realizeClass
realizeClass除了實現(xiàn)本類外,還實現(xiàn)本類的父類和元類.在此之前,cls->data()
指向的是ro
,在realizeClass
內(nèi),完成了cls->ro
到cls->rw->ro
的轉(zhuǎn)換(字面也能看出來:ro->readonly
,rw->readwrite
,之后rw
內(nèi)就可以寫入內(nèi)容了)
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
5.2 methodizeClass
向cls->rw
內(nèi)填充方法列表,屬性列表,協(xié)議列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
5.3 attachCategories
讀取類對應(yīng)分類內(nèi)的方法列表,屬性列表,協(xié)議列表,填入cls->rw
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);
}
- 6.resolvedFutureClasses
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
將第2步readClass
中產(chǎn)生的Class *resolvedFutureClasses
內(nèi)的類全部初始化.
- 7.讀取所有分類信息
可能你會說不是在attachCategories
方法內(nèi)讀取過類對應(yīng)分類的消息然后填入cls->rw
嗎?
以上已經(jīng)說過了realizeClass
初始化的是non-lazy class
(實現(xiàn)了+load
方法的類),所以還是有許多lazy class
沒有初始化.
category_t **catlist = _getObjc2CategoryList(hi, &count);
Class cls = remapClass(cat->cls);
讀取分類列表,由單個分類(cat)
反拿所屬類(cls)
,
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
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" : "");
}
}
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);
}
}
7.1 addUnattachedCategoryForClass
綁定類與分類的關(guān)系.
7.2 如果滿足(cat->instanceMethods||cat->protocols||cat->instanceProperties)
,則看類有沒有被初始化cls->isRealized()
,初始化了則調(diào)用remethodizeClass
.
remethodizeClass
比methodizeClass
簡單了很多,因為調(diào)用remethodizeClass
方法的情況下,類肯定是已經(jīng)被初始化了.當(dāng)然remethodizeClass
最后也調(diào)用了attachCategories
.
cat->instanceMethods
cat->protocols
cat->instanceProperties
以上三項對應(yīng)類;
cat->classMethods
cat->protocols
cat->_classProperties
以上三項則對應(yīng)類的元類;
當(dāng)然調(diào)用addUnattachedCategoryForClass
與remethodizeClass
的邏輯,類與元類是一樣的.
存疑: realizeClass->methodizeClass->attachCategories
discover categories->remethodizeClass->attachCategories
way1:初始化所有非懶加載類->填充3大列表->取出分類再填充3大列表
way2:拿出所有分類->反拿分類的類->類已經(jīng)初始化->取出分類再填充3大列表
好像做了重復(fù)的工作,存疑
3.2 load_images
void load_images(const char *path __unused, const struct mach_header *mh)
{
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
load_images
的功能會比map_2_images
簡明很多,就是調(diào)用所有實現(xiàn)+load
方法的類與分類.
- prepare_load_methods(生產(chǎn))
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
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);
}
}
- 生產(chǎn)類
注意這邊獲取non_lazy class
列表的方法與先前講過獲取方法已經(jīng)不一樣了:
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
extern classref_t *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
extern classref_t *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);
傳入的值由header_info
變成了headerType
.
在獲取所有non_lazy class
后,調(diào)用schedule_class_load
,將
non_lazy class
加入靜態(tài)結(jié)構(gòu)體數(shù)組static struct loadable_class *loadable_classes;
struct loadable_class {
Class cls; // may be nil
IMP method;
};
//cls->對應(yīng)類
//method->+load方法的地址
schedule_class_load
內(nèi)有一個特殊處理:就是用類的父類遞歸調(diào)用schedule_class_load
,這樣數(shù)組loadable_classes
內(nèi)父類就排在前邊,父類的+load
自然也就先調(diào)用.
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);
}
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++;
}
- 生產(chǎn)分類
處理好實現(xiàn)了+load
的類后就是要處理實現(xiàn)了+load
的分類了.
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);
}
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++;
}
獲取所有實現(xiàn)+load
的分類:
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
加入靜態(tài)結(jié)構(gòu)體數(shù)組static struct loadable_category *loadable_categories;
struct loadable_category {
Category cat; // may be nil
IMP method;
};
//cat->對應(yīng)分類
//method->+load方法的地址
- call_load_methods(消費)
在做完前面的生產(chǎn)準(zhǔn)備工作之后,就是要消費準(zhǔn)備好的類和分類了.
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();
printf("call_load_methods up \n");
do {
printf("call_load_methods in \n");
// 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);
printf("call_load_methods down \n");
objc_autoreleasePoolPop(pool);
loading = NO;
}
順序是先類后分類,所以+load
方法的調(diào)用順序可以總結(jié)為父類->類->分類
.
- 消費類
call_class_loads
遍歷準(zhǔn)備好的loadable_classes
內(nèi)的每一個struct loadable_class
,調(diào)用(*load_method)(cls, SEL_load);
- 消費分類
call_category_loads
遍歷準(zhǔn)備好的loadable_categories
內(nèi)的每一個struct loadable_category
,調(diào)用(*load_method)(cls, SEL_load);
3.3 unmap_image
跑了源碼沒有調(diào)用過unmap_image
,想來是針對一些讀取鏡像失敗的特殊情況的
在runtime初始化完成之際可以看到只有non_lazy class
加載好了,其他的類還沒加載到內(nèi)存中,那這些類有事什么時候才加載那內(nèi)存中呢?這些類會在程序第一次用到這些類的時候才初始化到內(nèi)存中,類的+initialize
方法也就是在此時被調(diào)用的.
所有流程可以合并以下流程圖:
文章參考:
objc源碼
我們的對象會經(jīng)歷什么