咱們一般的開發過程,對於類的使用能夠說是信手拈來,初始化、調用方法等等。那麼類是何時進行加載的呢?在app
啓動以後,若是實現了+load
方法,會先執行load
方法,那麼load
方法又是什麼時候執行的呢?經過研究類的加載,能夠獲得問題的答案。swift
libobjc:781
數組
Xcode 11.6
緩存
在dyld啓動流程一文中,有提到過libobjc
的初始化方法_objc_init
。markdown
environ_init()
: 讀取影響運⾏時的環境變量。若是須要,還能夠打印環境變量幫助。tls_init()
關於線程key的綁定 - ⽐如每線程數據的析構函數static_init()
運⾏C ++靜態構造函數。在dyld
調⽤咱們的靜態構造函數以前,libc
會調⽤ _objc_init()
,所以咱們必須⾃⼰作runtime_init()
: runtime運⾏時環境初始化,⾥⾯主要是:unattachedCategories
,allocatedClasses
的初始化,後⾯會分析exception_init ()
初始化libobjc的異常處理系統cache_init()
緩存條件初始化_imp_implementationWithBlock_init
:啓動回調機制。一般這不會作什麼,由於全部的初始化都是惰性的,可是對於某些進程,咱們會火燒眉毛地加載trampolines dylibapp
_dyld_objc_notify_register
: 咱們向dyld
註冊了三個回調,分別是:map_iamges
、load_iamges
、unmap_images
。 ide
咱們重點關注前兩個方法。函數
在當前方法中打下斷點,能夠發現map_iamges
的調用是在dyld的源碼之中, oop
具體的代碼邏輯能夠在dyld
源碼中進行查看。 咱們沿着map_iamges
接下往裏面走,執行了map_images_nolock
方法。核心方法以下:post
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
...
// Find all images with Objective-C metadata.
hCount = 0;
// 經過mach_header遍歷,找出全部的類數量
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
// 生成對應的header_info
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}
...
//添加到hList中
hList[hCount++] = hi;
...
}
}
...
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
...
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
複製代碼
_read_images
方法主要作了以下操做: ui
咱們先來關注一下第9個,類的加載處理
// 實現非懶加載類(+load方法和靜態實例)
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
// 遍歷各個header_info,從對應的macho中讀取出非懶加載類的列表
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 添加cls到
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 實現cls
realizeClassWithoutSwift(cls, nil);
}
}
複製代碼
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
// 類cls的第一次初始化,包括開闢它的可讀寫數據class_rw_t,返回類的真正結構
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 開闢class_rw_t尺寸大小的空間
rw = objc::zalloc<class_rw_t>();
// 賦值rw->ro
rw->set_ro(ro);
// 修改rw中相關標識flags
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
// 賦值cls—>rw
cls->setData(rw);
}
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 遞歸實現父類
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
// 遞歸實現元類
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
// 更新父類和本身的ISA指向即元類
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
// 設置實例的大小
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
// 從ro中拷貝一些標識符到rw中
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
// 若是ro中禁止了關聯對象或者父類禁止了關聯對象,修改rw相關標識
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
// 當前類添加到父類的子類列表中
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls, previously);
return cls;
}
複製代碼
咱們先來看一下關於類的結構定義:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits;
}
複製代碼
在上述代碼中讀取ro
的代碼爲:auto ro = (const class_ro_t *)cls->data()
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
複製代碼
在源碼中data()
返回的實際上是rw
的指針。咱們知道ro
是在編譯期就肯定下來的,能夠理解爲在未實現的cls
結構中,本來屬於rw
的位置存放的是ro
的指針。
1. 實現類的第一步操做就是開闢rw
,正確創建ro和rw以及cls
自己的關係
2. 遞歸實現當前cls的父類和元類
3. 將ro中的一些標識拷貝到rw中
4. 修正cls中的方法、協議、屬性以及未連接的分類
/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// 裝載cls本身實現的方法和屬性
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
// 根元類增長initialize的實現
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
// 在特定狀況下若是有未加載的分類,加載到cls中,具體的場景後續會講到
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
...
}
複製代碼
接下來看一下methodizeClass
,它的主要做用是調整cls實現的方法列表、屬性列表以及協議列表以及加載特定狀況下的分類。
在該方法中,會處理cls的方法列表。
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
...
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
// Fixup selectors if necessary
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
...
}
複製代碼
遍歷二維數組addedLists
,取出其中的每個list
進行fixupMethodList
修復。
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
...
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// 惟一標識list中的method
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
meth.name = sel_registerNameNoLock(name, bundleCopy);
}
}
// Sort by selector address.
// 根據selector地址進行排序,這個能夠解釋在懶加載狀況下分類和主類的同名方法是排在一塊兒的
if (sort) {
method_t::SortBySELAddress sorter;
std::stable_sort(mlist->begin(), mlist->end(), sorter);
}
// Mark method list as uniqued and sorted
mlist->setFixedUp();
}
複製代碼
接下來關注load_images
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
// 加載全部的分類
loadAllCategories();
}
// 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, 發現load方法
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant), 執行load方法
call_load_methods();
}
複製代碼
static void loadAllCategories() {
mutex_locker_t lock(runtimeLock);
for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
load_categories_nolock(hi);
}
}
複製代碼
遍歷所有的header數據,加載其中的分類。
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
// processCatlist相似於一個函數指針,會在該方法的末尾進行調用
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
// 遍歷catlist獲得當前的分類cat
category_t *cat = catlist[i];
// 獲取cat對應的cls
Class cls = remapClass(cat->cls);
// 初始化lc結構體
// struct locstamped_category_t {
// category_t *cat;
// struct header_info *hi;
// };
locstamped_category_t lc{cat, hi};
...
// Process this category.
if (cls->isStubClass()) {
...
} else {
// 先將category註冊到它對應的class中,若是class已經實現則重構class的方法列表,由於此時方法列表發生了改變,若是class尚未實現只將當前category和class添加聯繫。同時還判斷了類方法和實例方法
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
if (cls->isRealized()) {
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls);
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
if (cls->ISA()->isRealized()) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
} else {
objc::unattachedCategories.addForClass(lc, cls->ISA());
}
}
}
}
};
// 從macho中讀取分類列表以及更新分類個數count
processCatlist(_getObjc2CategoryList(hi, &count));
processCatlist(_getObjc2CategoryList2(hi, &count));
}
複製代碼
// 將分類加載到主類
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
...
// 初始化必要的數組,默認最多會有64個分類
constexpr uint32_t ATTACH_BUFSIZ = 64;
// 存放method_list_t的數組
method_list_t *mlists[ATTACH_BUFSIZ];
// property_list_t的數組
property_list_t *proplists[ATTACH_BUFSIZ];
// protocol_list_t的數組
protocol_list_t *protolists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
uint32_t propcount = 0;
uint32_t protocount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
// 獲取rwe或者開闢rwe
auto rwe = cls->data()->extAllocIfNeeded();
for (uint32_t i = 0; i < cats_count; i++) {
// 讀取locstamped_category_t結構體
auto& entry = cats_list[i];
// 獲取cat對應的類方法或者實例方法
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
// 若是數組滿了,先修復cls方法,而後重置mcount
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
複製代碼
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
// 原始的數組個數
uint32_t oldCount = array()->count;
// 新的數組個數
uint32_t newCount = oldCount + addedCount;
// 開闢新數組個數大小的空間
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
// 從新賦值
array()->count = newCount;
// 將原有的數組日後平移插入的元素個數的大小
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 插入到數組的首個位置
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
複製代碼
該方法主要負責將新的列表插入到原始列表的首位置,其餘元素日後平移。
class_rw_ext_t
是蘋果在wwdc20提出的一個新的改動,主要是爲了減小類所佔用的內存,結構以下:
struct class_rw_ext_t {
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
複製代碼
該方法的做用是開闢class_rw_ext_t
而且將ro
中的方法、屬性以及協議列等數據複製到rwe
的對應位置。
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
開闢class_rw_ext_t大小的內容空間
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods();
if (list) {
// 複製一份ro中的方法列表存儲到rwe的methods中
if (deepCopy) list = list->duplicate();
rwe->methods.attachLists(&list, 1);
}
// 複製方法列表
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rwe->properties.attachLists(&proplist, 1);
}
//複製協議列表
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// 正確處理rwe和ro的關係
set_ro_or_rwe(rwe, ro);
return rwe;
}
複製代碼
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
// 讀取當前macho中的非懶加載類列表
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 添加列表中每個cls以及load方法的實現到一個數組中
schedule_class_load(remapClass(classlist[i]));
}
// 獲取非懶加載分類的列表
category_t * const *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, nil);
ASSERT(cls->ISA()->isRealized());
// 添加分類到數組中
add_category_to_loadable_list(cat);
}
}
複製代碼
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// 遞歸添加,先添加父類,再添加子類,這樣能夠確保父類的load方法在子類的load方法以前調用
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
複製代碼
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) {
// 調用主類的load方法
call_class_loads();
}
// 2. Call category +loads ONCE
// 調用分類的load方法
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;
}
複製代碼