在上一篇文章 objc_init 分析 中,最后有三個(gè)函數(shù) map_images
莺奔、load_images
、unmap_image
愚隧。
一钥组、map_images
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
1.1 map_images_nolock
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
// 省略準(zhǔn)備邏輯...
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
因?yàn)榇a比較多信夫,省略了很多代碼,瀏覽代碼后铣减,會(huì)發(fā)現(xiàn)重要的是 _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
這一行代碼她君。
二、_read_images
因?yàn)樵创a比較長(zhǎng)葫哗,這里就不貼了缔刹。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
// 省略代碼...
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
// 斷點(diǎn)1
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized); // 讀取類
}
}
// 省略 protocol 處理...
// Realize non-lazy classes (for +load methods and static instances)
// 實(shí)現(xiàn)非惰性類(用于+ load方法和靜態(tài)實(shí)例)
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
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
}
realizeClassWithoutSwift(cls, nil);
}
}
}
主要流程如下:
- 條件控制進(jìn)行一次的加載
- 修復(fù)預(yù)編譯階段的
@selector
的混亂問(wèn)題 - 錯(cuò)誤混亂的類處理
- 修復(fù)重映射?些沒(méi)有被鏡像?件加載進(jìn)來(lái)的類
- 修復(fù)?些消息
- 當(dāng)我們類??有協(xié)議的時(shí)候 :
readProtocol
- 修復(fù)沒(méi)有被加載的協(xié)議
- 分類處理
- 類的加載處理
- 沒(méi)有被處理的類 優(yōu)化那些被侵犯的類
在上面代碼中 斷點(diǎn)1
打個(gè)斷點(diǎn),運(yùn)行
(lldb) po cls
0x000000010048dfe8
--- 走過(guò)斷點(diǎn)1和readClass之后再打印
(lldb) po newCls
OS_dispatch_io
在沒(méi)有 readClass
之前劣针,classlist
里面存的還是內(nèi)存地址校镐,readClass
之后,就有名字了捺典。下面看下 readClass
具體做了什么?
2.1 readClass
讀取編譯器編寫的類和元類鸟廓。返回新的類指針。
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
if (missingWeakSuperclass(cls)) {
// 省略...
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
// 省略...
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
// 重點(diǎn)
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
在 mangleName
打個(gè)斷點(diǎn)
發(fā)現(xiàn)是系統(tǒng)的類。我們還是研究自定義類 GLPerson
引谜,在mangleName
下面添加如下代碼:
// godlong test begin
const char *GLPersonName = "GLPerson";
if (strcmp(mangledName, GLPersonName) == 0) {
printf("%s 來(lái)到了自定義類 %s", __func__, mangledName);
}
// godlong test end
斷點(diǎn)到 if
條件里面牍陌,運(yùn)行工程,成功進(jìn)入斷點(diǎn)
2.2 addNamedClass
走到 addNamedClass
斷點(diǎn)查看
可以發(fā)現(xiàn)這時(shí) cls
還是地址员咽,而 mangledName
就是我們的類名毒涧。
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
}
解釋
:將類的名稱添加到非元類的類映射表中。警告有關(guān)重復(fù)的類名贝室,并保留舊的映射契讲。
2.3 addClassTableEntry
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
auto &set = objc::allocatedClasses.get();
ASSERT(set.find(cls) == set.end());
if (!isKnownClass(cls))
set.insert(cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
解釋
: 將一個(gè)類添加到所有類的表中。如果 addMeta
為 true
滑频,也自動(dòng)添加該類的元類捡偏。
三、realizeClassWithoutSwift
先重點(diǎn)關(guān)注類
的加載峡迷,在 read_images
的源碼中银伟,往下看的時(shí)候,發(fā)現(xiàn)注釋
Realize non-lazy classes (for
+load
methods and static instances)
【譯】實(shí)現(xiàn)非懶加載類(用于+ load
方法和靜態(tài)實(shí)例)
那什么是 non-lazy classes
呢凉当?
3.1 non-lazy classes 非懶加載類
如果一個(gè)類實(shí)現(xiàn)了 + load
方法枣申,那這個(gè)類就是 non-lazy class
(非懶加載類)。
反之看杭,沒(méi)實(shí)現(xiàn) + load
方法忠藤,就是懶加載類。
3.2 進(jìn)入自定義類的 realizeClassWithoutSwift
給 GLPerson
類添加 + load
方法楼雹,然后把 for(EACH_HEADER)
循環(huán)里面的代碼改成如下:
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// loong test begin
const char *mangledName = cls->mangledName();
const char *GLPersonName = "GLPerson";
if (strcmp(mangledName, GLPersonName) == 0) {
printf("%s 來(lái)到了自定義類 %s", __func__, mangledName); // 斷點(diǎn)2
}
// loong test end
addClassTableEntry(cls);
// class is a Swift class from the stable Swift ABI 所以不會(huì)走這里
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
}
realizeClassWithoutSwift(cls, nil);
}
}
斷點(diǎn)到上面代碼的 斷點(diǎn)2
模孩,確認(rèn)來(lái)到了自定義類 GLPerson
。
3.3【重點(diǎn)】realizeClassWithoutSwift
源碼
realizeClassWithoutSwift
雖然很長(zhǎng)贮缅,但是都比較重要榨咐,所以也沒(méi)有省略,全貼出來(lái)了谴供。
方法注釋:
- 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.
【譯】* 對(duì)類cls
進(jìn)行首次初始化块茁,包括分配其讀寫數(shù)據(jù)。不執(zhí)行任何Swift
端初始化桂肌。返回該類的真實(shí)類結(jié)構(gòu)数焊。
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw; // 讀寫數(shù)據(jù)
Class supercls; // 父類
Class metacls; // 元類
if (!cls) return nil; // 如果為空,返回nil
if (cls->isRealized()) return cls; // 如果已經(jīng)實(shí)現(xiàn)崎场,直接返回
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(); // 讀取類的數(shù)據(jù)
auto isMeta = ro->flags & RO_META; // 是否是元類
if (ro->flags & RO_FUTURE) { // rw已經(jīng)有值的話走這里
// 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.
rw = objc::zalloc<class_rw_t>(); // 開(kāi)辟rw
rw->set_ro(ro); // 把cls的數(shù)據(jù)ro賦值給rw
rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 更新flags
cls->setData(rw); // 再把rw設(shè)置為cls的data數(shù)據(jù)
}
#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.
//實(shí)現(xiàn)超類和元類(如果尚未實(shí)現(xiàn))佩耳。
// This needs to be done after RW_REALIZED is set above, for root classes.
//對(duì)于根類,需要在上面設(shè)置了RW_REALIZED之后執(zhí)行此操作谭跨。
// This needs to be done after class index is chosen, for root metaclasses.
//對(duì)于根元類干厚,需要在選擇類索引之后執(zhí)行此操作李滴。
// 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.
// 遞歸調(diào)用 realizeClassWithoutSwift ,實(shí)現(xiàn)父類和元類
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) { // 如果是元類蛮瞄,對(duì)isa處理
// 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 { // 不是元類所坯,也是對(duì)isa處理
// 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
// 確定繼承鏈,賦值父類和元類
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// 協(xié)調(diào)實(shí)例變量的偏移量/布局裕坊。
// 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.
// 經(jīng)過(guò)上一步包竹,再次協(xié)調(diào)屬性對(duì)齊后,設(shè)置實(shí)例大小
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
// 賦值一些 ro 中的 flags標(biāo)識(shí)位 到 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或父類傳播關(guān)聯(lián)的對(duì)象禁止標(biāo)志籍凝。
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
// 添加當(dāng)前類到父類的子類列表中,如果沒(méi)有父類苗缩,設(shè)置自己就是根類
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 附加分類
methodizeClass(cls, previously);
return cls;
}
確認(rèn)是 GLPerson
的時(shí)候進(jìn)入 realizeClassWithoutSwift
饵蒂,在 auto ro = (const class_ro_t *)cls->data();
這行代碼打一個(gè)端點(diǎn),這一步是 cls
讀取 data
數(shù)據(jù)賦值給 ro
酱讶。
然后往下走一步退盯,看看讀取到了什么數(shù)據(jù)?
3.4 遞歸調(diào)用 realizeClassWithoutSwift
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
realizeClassWithoutSwift
的源碼中泻肯,遞歸調(diào)用了 realizeClassWithoutSwift
渊迁,分別傳入的是父類和元類(ISA()
)。
這是為了確定繼承鏈的關(guān)系灶挟。
3.5 懶加載類實(shí)現(xiàn)時(shí)機(jī)
知道了非懶加載類在 map_images
時(shí)琉朽,調(diào)用 realizeClassWithoutSwift
實(shí)現(xiàn)的,那懶加載類是什么時(shí)候?qū)崿F(xiàn)的呢稚铣?
還是上面的代碼箱叁,把 GLPerson
中的 + load
方法去掉,然后再realizeClassWithoutSwift
方法里面添加如下代碼并斷點(diǎn)在 斷點(diǎn)3
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
// loong test begin
if (cls) {
const char *mangledName = cls->mangledName();
const char *GLPersonName = "GLPerson";
if (strcmp(mangledName, GLPersonName) == 0) {
printf("%s 來(lái)到了自定義類 %s", __func__, mangledName); // 斷點(diǎn)3
}
}
// loong test end
// 省略...
}
在 main.m
中添加惕医,并添加斷點(diǎn)
GLPerson *p = [GLPerson alloc];
運(yùn)行工程耕漱,首先斷點(diǎn)在 GLPerson *p = [GLPerson alloc];
繼續(xù)運(yùn)行,發(fā)現(xiàn)走到了 斷點(diǎn)3
抬伺。查看堆棧
可知:懶加載類
是在第一次發(fā)送消息的時(shí)候螟够,調(diào)用 realizeClassWithoutSwift
實(shí)現(xiàn)類的。
3.6 懶加載類的優(yōu)點(diǎn)
因?yàn)檎5墓こ讨邢康觯瑢?shí)現(xiàn) + load
方法的類很少妓笙,大部分類都是懶加載類。
如果所有的類都在啟動(dòng)的時(shí)候?qū)崿F(xiàn)完成椒楣,就會(huì)非常慢给郊,懶加載類等到調(diào)用的時(shí)候再去實(shí)現(xiàn),這樣會(huì)加快啟動(dòng)速度捧灰。
再就是每個(gè)類都有很多的代碼淆九,包括變量统锤,方法等,會(huì)占用很多內(nèi)存炭庙,而如果你這個(gè)類在工程中就沒(méi)調(diào)用饲窿,或者在很深的頁(yè)面才會(huì)調(diào)用,正常情況下很少人會(huì)使用到焕蹄,如果在啟動(dòng)加載了逾雄,就會(huì)造成內(nèi)存浪費(fèi)。
懶加載類優(yōu)點(diǎn):
- 加快啟動(dòng)速度
- 節(jié)省應(yīng)用初始內(nèi)存
四腻脏、methodizeClass
附加分類
作用:修復(fù) cls
的方法列表鸦泳,協(xié)議列表和屬性列表。附加任何未解決的分類永品。
源碼:
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro(); // 讀取ro數(shù)據(jù)
auto rwe = rw->ext(); // 讀取ext做鹰,賦值給rwe
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods(); // 獲取ro中的方法列表
if (list) {
// 對(duì)方法列表list重新排序
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1); // 如果有rwe,添加方法列表list到rwe的methodsList
}
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);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}