iOS底層探索之_objc_init

一赤嚼、_objc_init源碼

_objc_init 也是在 libObjc 的源碼中

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    // 讀取影響運行時的環(huán)境變量等孵。如果需要蹂空,還可以打印環(huán)境變量幫助
    environ_init();
    // 關(guān)于線程key的綁定,比如:線程數(shù)據(jù)的析構(gòu)函數(shù)
    tls_init();
    // 運行C++靜態(tài)構(gòu)造函數(shù)咐熙。在dyld調(diào)用我們的靜態(tài)構(gòu)造函數(shù)之前棋恼,libc 會調(diào)用 _objc_init()
    static_init();
    // runtime運行時環(huán)境初始化
    runtime_init();
    // libobjc異常處理系統(tǒng)初始化
    exception_init();
    // 緩存條件初始化
    cache_init();
    // 啟動回調(diào)機制爪飘。通常不會做什么师崎,因為所有的初始化都是惰性的
    _imp_implementationWithBlock_init();
     /*
     _dyld_objc_notify_register -- dyld 注冊的地方
     - 僅供objc運行時使用
     - 注冊處理程序犁罩,以便在映射床估、取消映射 和初始化objc鏡像文件時使用鬼雀,dyld將使用包含objc_image_info的鏡像文件數(shù)組源哩,回調(diào) mapped 函數(shù)
     
     map_images:dyld將image鏡像文件加載進內(nèi)存時鸦做,會觸發(fā)該函數(shù)
     load_images:dyld初始化image會觸發(fā)該函數(shù)
     unmap_image:dyld將image移除時會觸發(fā)該函數(shù)
     */
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

_objc_init 這個方法里面基本上本身沒有什么邏輯坛掠,又把邏輯封裝了一下,下面看下里面每個方法的作用

二舷蒲、environ_init() 環(huán)境變量初始化

  • environ_init
  • Read environment variables that affect the runtime.
  • Also print environment variable help, if requested.
    【譯】
  • 讀取影響運行時的環(huán)境變量牲平。
  • 如果需要纵柿,還可以打印環(huán)境變量幫助昂儒。
void environ_init(void) 
{
    // 省略...
    // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
    if (PrintHelp  ||  PrintOptions) {
        if (PrintHelp) {
            _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
            _objc_inform("OBJC_HELP: describe available environment variables");
            if (PrintOptions) {
                _objc_inform("OBJC_HELP is set");
            }
            _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
        }
        if (PrintOptions) {
            _objc_inform("OBJC_PRINT_OPTIONS is set");
        }

        for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
            const option_t *opt = &Settings[i];            
            if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
            if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
        }
    }
}

把里面的 for 循環(huán)拿出來放到外面,把判斷條件去掉

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
        const option_t *opt = &Settings[i];
        _objc_inform("%s: %s", opt->env, opt->help);
        _objc_inform("%s is set", opt->env);
    }

運行工程刹枉,會在控制臺輸出如下環(huán)境變量幫助:

objc[15188]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[15188]: OBJC_PRINT_IMAGES is set
objc[15188]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[15188]: OBJC_PRINT_IMAGE_TIMES is set
objc[15188]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[15188]: OBJC_PRINT_LOAD_METHODS is set
objc[15188]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[15188]: OBJC_PRINT_INITIALIZE_METHODS is set
objc[15188]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[15188]: OBJC_PRINT_RESOLVED_METHODS is set
objc[15188]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[15188]: OBJC_PRINT_CLASS_SETUP is set
objc[15188]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[15188]: OBJC_PRINT_PROTOCOL_SETUP is set
objc[15188]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[15188]: OBJC_PRINT_IVAR_SETUP is set
objc[15188]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[15188]: OBJC_PRINT_VTABLE_SETUP is set
objc[15188]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[15188]: OBJC_PRINT_VTABLE_IMAGES is set
objc[15188]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[15188]: OBJC_PRINT_CACHE_SETUP is set
objc[15188]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[15188]: OBJC_PRINT_FUTURE_CLASSES is set
objc[15188]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[15188]: OBJC_PRINT_PREOPTIMIZATION is set
objc[15188]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[15188]: OBJC_PRINT_CXX_CTORS is set
objc[15188]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[15188]: OBJC_PRINT_EXCEPTIONS is set
objc[15188]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[15188]: OBJC_PRINT_EXCEPTION_THROW is set
objc[15188]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[15188]: OBJC_PRINT_ALT_HANDLERS is set
objc[15188]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[15188]: OBJC_PRINT_REPLACED_METHODS is set
objc[15188]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[15188]: OBJC_PRINT_DEPRECATION_WARNINGS is set
objc[15188]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[15188]: OBJC_PRINT_POOL_HIGHWATER is set
objc[15188]: OBJC_PRINT_CUSTOM_CORE: log classes with custom core methods
objc[15188]: OBJC_PRINT_CUSTOM_CORE is set
objc[15188]: OBJC_PRINT_CUSTOM_RR: log classes with custom retain/release methods
objc[15188]: OBJC_PRINT_CUSTOM_RR is set
objc[15188]: OBJC_PRINT_CUSTOM_AWZ: log classes with custom allocWithZone methods
objc[15188]: OBJC_PRINT_CUSTOM_AWZ is set
objc[15188]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[15188]: OBJC_PRINT_RAW_ISA is set
objc[15188]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[15188]: OBJC_DEBUG_UNLOAD is set
objc[15188]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[15188]: OBJC_DEBUG_FRAGILE_SUPERCLASSES is set
objc[15188]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[15188]: OBJC_DEBUG_NIL_SYNC is set
objc[15188]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[15188]: OBJC_DEBUG_NONFRAGILE_IVARS is set
objc[15188]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[15188]: OBJC_DEBUG_ALT_HANDLERS is set
objc[15188]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[15188]: OBJC_DEBUG_MISSING_POOLS is set
objc[15188]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[15188]: OBJC_DEBUG_POOL_ALLOCATION is set
objc[15188]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[15188]: OBJC_DEBUG_DUPLICATE_CLASSES is set
objc[15188]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[15188]: OBJC_DEBUG_DONT_CRASH is set
objc[15188]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[15188]: OBJC_DISABLE_VTABLES is set
objc[15188]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[15188]: OBJC_DISABLE_PREOPTIMIZATION is set
objc[15188]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[15188]: OBJC_DISABLE_TAGGED_POINTERS is set
objc[15188]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[15188]: OBJC_DISABLE_TAG_OBFUSCATION is set
objc[15188]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[15188]: OBJC_DISABLE_NONPOINTER_ISA is set
objc[15188]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[15188]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY is set

找一個 環(huán)境變量 OBJC_PRINT_LOAD_METHODS : log calls to class and category +load methods蟋软,記錄對類和類別 + load 方法的調(diào)用

Edit Scheme 設置

設置環(huán)境變量.png

在自定義類中岳守,添加 + load 方法

@implementation GLPerson

+ (void)load {}
@end

運行工程:

類的load方法調(diào)用.png

會發(fā)現(xiàn)湿痢,不管是系統(tǒng)的,還是自定義的類扑庞,所有實現(xiàn) + load 方法的類都會打印出來譬重。

通過終端命令 export OBJC_HELP=1 也可以查看環(huán)境變量幫助

終端打印OBJC_HELP.png

三、tls_init 本地線程池初始化

主要作用是: 本地線程池的初始化和析構(gòu)罐氨。

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++靜態(tài)構(gòu)造函數(shù)

  • 運行 C ++ 靜態(tài)構(gòu)造函數(shù)。
  • libcdyld 調(diào)用我們的靜態(tài)構(gòu)造函數(shù)之前調(diào)用_objc_init()栅隐,因此塔嬉,我們必須自己做玩徊。
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 runtime運行時環(huán)境初始化

void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}

六谨究、exception_init 異常處理系統(tǒng)初始化

初始化 libobjc 的異常處理系統(tǒng)恩袱。由map_images() 調(diào)用

void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}

七俩檬、cache_init 緩存條件初始化

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 啟動回調(diào)機制

該方法主要是啟動回調(diào)機制联逻,通常這不會做什么,因為所有的初始化都是惰性的公壤,但是對于某些進程慨飘,我們會迫不及待地加載 libobjc-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_notify_register dyld注冊

// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
// dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
// initializers in that image.  This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);

這個方法本身只是一個注冊函數(shù)的聲明,在 objc 源碼中找不到實現(xiàn)了,具體實現(xiàn)還需去 dyld 源碼中去查看祈匙。

_dyld_objc_notify_register(&map_images, load_images, unmap_image); 傳了3個參數(shù):

  1. map_images : 引用傳遞,dyldimage(鏡像文件)加載進內(nèi)存時,會觸發(fā)該函數(shù);
  2. load_images : 值傳遞市埋,dyld 初始化 image 會觸發(fā)該函數(shù);
  3. unmap_image : 值傳遞褐着,dyldimage 移除時洋访,會觸發(fā)該函數(shù);
dyld_objc.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汁展,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铲汪,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異创南,居然都是意外死亡扰藕,警方通過查閱死者的電腦和手機芥备,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門日月,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尺借,“玉大人栅表,你說我怎么就攤上這事∠捶。” “怎么了哆姻?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵帖旨,是天一觀的道長落竹。 經(jīng)常有香客問我蟹地,道長,這世上最難降的妖魔是什么夺刑? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任耘斩,我火速辦了婚禮倾哺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刽脖。我一直安慰自己曲管,他們只是感情好院水,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挺据。 梳的紋絲不亂的頭發(fā)上做葵,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音棺禾,去河邊找鬼缀蹄。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的缺前。 我是一名探鬼主播蛀醉,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衅码!你這毒婦竟也來了拯刁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤逝段,失蹤者是張志新(化名)和其女友劉穎垛玻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惹恃,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡夭谤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年棺牧,在試婚紗的時候發(fā)現(xiàn)自己被綠了巫糙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡颊乘,死狀恐怖参淹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乏悄,我是刑警寧澤浙值,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站檩小,受9級特大地震影響开呐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜规求,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一筐付、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阻肿,春花似錦瓦戚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赴邻,卻和暖如春印衔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姥敛。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工当编, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓忿偷,卻偏偏與公主長得像金顿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鲤桥,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348