iOS 底層原理之—dyld 與 objc 的關聯

前言

 

dyld加載過程當中,咱們知道會調用_objc_init方法,那麼在_objc_init方法中究竟作了什麼呢?咱們來探究下。面試

 

_objc_init方法

 

_objc_init方法實現

 

 
 
 
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
 
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
 
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
 
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
 
 
 
 

_objc_init實現中咱們分析下該方法主要作了什麼緩存

 

environ_init()

 

該方法主要是讀取運行時的環境變量,咱們能夠經過設置DYLD_PRINT_STATISTICS = YES來打印APP啓動到main()函數以前的時長,進而能夠進行APP啓動優化。具體的environ_init()簡介可參考博客iOS-底層原理 16:dyld與objc的關聯中有關nviron_init()部分的介紹app

 

tls_init()

 

主要用於關於線程key的綁定,好比每線程數據的析構函數。函數

 

 
 
 
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
 
 
 
 

static_init()

 

主要是C++靜態構造函數優化

 

 
 
 
static void static_init()
{
size_t count;
auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
for (size_t i = 0; i < count; i++) {
inits[i]();
}
}
 
 
 
 

runtime_init()

 

主要是運行時的初始化,主要分爲兩部分:分類初始化類的表初始化ui

 

 
 
 
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}
複製代碼
 
 
 
 

exception_init()

 

初始化libobjc異常處理this

 

 
 
 
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
 
 
 
 

cache_init()

 

主要是緩存初始化spa

 

 
 
 
void cache_init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
mach_msg_type_number_t count = 0;
kern_return_t kr;
 
while (objc_restartableRanges[count].location) {
count++;
}
 
kr = task_restartable_ranges_register(mach_task_self(),
objc_restartableRanges, count);
if (kr == KERN_SUCCESS) return;
_objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}
 
 
 
 

_imp_implementationWithBlock_init()

 

主要用來啓動機制回調線程

 

 
 
 
/// everything is initialized lazily, but for certain processes we eagerly load
/// the trampolines dylib.
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
// programs (most notably QtWebEngineProcess used by older versions of
// embedded Chromium) enable a highly restrictive sandbox profile which
// blocks access to that dylib. If anything calls
// imp_implementationWithBlock (as AppKit has started doing) then we'll
// crash trying to load it. Loading it here sets it up before the sandbox
// profile is enabled and blocks it.
//
// This fixes EA Origin (rdar://problem/50813789)
// and Steam (rdar://problem/55286131)
if (__progname &&
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
strcmp(__progname, "Steam Helper") == 0)) {
Trampolines.Initialize();
}
#endif
}
 
 
 
 

dyld與objc關聯

 

_dyld_objc_notify_register(&map_images, load_images, unmap_image)

 

主要是dyld註冊 實際代碼實現3d

 

 
 
 
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
 
 
 
 

從上文正中咱們能夠看出

 

  • mappedmap_images

  • initload_images

  • unmappedunmap_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);
}
 
 
 
 

map_images函數中咱們發現map_images_nolock函數是重點,咱們進入map_images_nolock函數

 

map_images_nolock

 

咱們查看代碼實現

 

從截圖中咱們能夠看出_read_images是咱們要重點研究的方法

 

_read_images函數分析

 

是不是第一次加載

 

修復預編譯時@selector的錯亂問題

 

錯誤類處理,經過readClass讀取出來類的信息

 

從新設置映射鏡像

 

消息處理

 

類中若是有協議,讀取協議

 

映射協議

 

加載分類

 

注意在分類處理中主要是經過load_categories_nolock處理,咱們進入load_categories_nolock函數中

 

load_categories_nolock函數

 

 
 
 
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
 
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
locstamped_category_t lc{cat, hi};
 
if (!cls) {
// Category's target class is missing (probably weak-linked).
// Ignore the category.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
 
// Process this category.
if (cls->isStubClass()) {
// Stub classes are never realized. Stub classes
// don't know their metaclass until they're
// initialized, so we have to add categories with
// class methods or properties to the stub itself.
// methodizeClass() will find them and add them to
// the metaclass as appropriate.
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties ||
cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties))
{
objc::unattachedCategories.addForClass(lc, cls);
}
} else {
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
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());
}
}
}
}
};
 
processCatlist(_getObjc2CategoryList(hi, &count));
processCatlist(_getObjc2CategoryList2(hi, &count));
}
 
 
 
 

load_categories_nolock函數實現中,咱們能夠看到該函數將實例方法協議屬性類方法等再次連接了一次。

 

非懶加載類處理

 

處理沒有使用的類

 

dyld與objc關聯總結

 

  • dyld_start調用_objc_init來初始化,_objc_init中經過dyld調用_dyld_objc_notify_register函數,傳入map_imagesload_images這兩個參數來處理

  • map_images經過map_images_nolock函數調用_read_images函數

  • _read_images函數中處理類信息、屬性、協議、分類等

  • 當一切準備穩當,則再次返回dyld_start中,此時dyldobjc關聯了起來

 

資料推薦

 

若是你正在跳槽或者正準備跳槽不妨動動小手,添加一下我們的交流羣1012951431來獲取一份詳細的大廠面試資料爲你的跳槽多添一份保障。

 

相關文章
相關標籤/搜索