細說Load和Initialize

封面.jpg

load方法:

當一個類或者該類的分類被加入Objective-C運行時的時候被調(diào)用牵署。load method調(diào)用的時機是非常早的喧半,所以你不應該在該方法中去引用其他你自定義的對象挺据,因為你沒辦法去判斷該對象是否已經(jīng)進行 load method。當然暇检,你可以使用該類所依賴的frameworks婉称,比如Foundation,在你調(diào)用load method方法時這些框架已經(jīng)確保被完全加載成功悔据。當然你的父類也會在這前加載成功瘫筐。

load調(diào)用時機:

1.類本身的load method在所有父類的load method調(diào)用后調(diào)用。
2.分類的load method在父類的load method調(diào)用前調(diào)用肛捍。
3.類本身load method在分類的load method前調(diào)用拙毫。

我們現(xiàn)在建立4個文件,在每個文件里面添加load method,并在load method里面打印一句話:

1.png

不做任何操作直接bulid峭跳,我們來看打印臺的log
2.png
蛀醉。
這樣的結果就證明了我們前面的結論衅码。

源碼證明調(diào)用時機:

你可以點擊
這里下載runtime源碼逝段。這里下載的是objc4-706.tar.gz版本。
dyld 是the dynamic link editor的縮寫帚桩,動態(tài)鏈接器.主要的任務是為了生成可執(zhí)行文件嘹黔。
更多了解可以點擊這里
1.引導程序初始化:

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
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();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}

2.獲取在類的列表和分類的列表参淹,如果沒有則return;如果獲取到則上鎖調(diào)用prepare_load_methods

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();
}

3.prepare_load_methods方法中你可以看到先遍歷了類的列表而不是分類的乏悄,所以類的load method會先調(diào)用檩小。schedule_class_load方法中你可以看到如果父類有load method,會遞歸的去遍歷筐付,add_class_to_loadable_list然后加入到待加載列表阻肿。所以父類的load method方法會比子類先調(diào)用。而_getObjc2NonlazyCategoryList直接遍歷不做任何操作所以子類的在父類的前面较解。

 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);
    }
}
  //遞歸加入到到待加載列表
static void schedule_class_load(Class cls)
{
    if (cls->info & CLS_LOADED) return;
    if (cls->superclass) schedule_class_load(cls->superclass);
    add_class_to_loadable_list(cls);
    cls->info |= CLS_LOADED;
}

Initialize方法

runtime會發(fā)送initialize message初始化一個類印衔。父類會比子類先接受到這個消息。這個操作是線程安全的瞎暑,這就是說与帆,當你在某一個線程A里面初始化這個類玄糟,這個線程A會去發(fā)送Initialize message給這個類對象。如果其他線程B這個時候要發(fā)送其他的消息給這個類嫂拴,線程B會阻塞直到線程A處理Initialize消息完成贮喧。

Initialize調(diào)用時機:

1.父類會比子類先接受到這個消息。
2.如果子類沒有實現(xiàn)initialize mothod,而父類實現(xiàn)了辩恼。那么父類的Initialize mothod將會調(diào)用多次谓形。

我們現(xiàn)在在'Person'和'Student'文件里面重寫initialize method,讓后在'viewDidLoad method'添加下面的代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
          NSLog(@"%@ %s 當前的線程:%@", [self class], __FUNCTION__,[NSThread currentThread]);
          Student *Student1 = [[Student alloc] init];
          Student *Student2 = [[Student alloc] init];
    });
}

bulid項目寒跳,我們來看打印臺的log:

3.png

你會發(fā)現(xiàn):
1.我們做了兩次初始化但第二次初始化Student并沒有觸動initialize method童太。
2.父類的initialize method方法比子類的先調(diào)用。
3.Student初始化的線程和initialize method線程是相同的翘贮。

現(xiàn)在我們注釋掉子類的initialize method爆惧,再次bulid:

4.png

這個時候Person父類調(diào)用了兩次扯再。

源碼證明調(diào)用時機:

void _class_initialize(Class cls)
{
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }
    
    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);
        
        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: calling +[%s initialize]",
                         cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        @try {
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: finished +[%s initialize]",
                             cls->nameForLogging());
            }
        }
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: +[%s initialize] threw an exception",
                             cls->nameForLogging());
            }
            @throw;
        }
        @finally {
            // Done initializing. 
            // If the superclass is also done initializing, then update 
            //   the info bits and notify waiting threads.
            // If not, update them later. (This can happen if this +initialize 
            //   was itself triggered from inside a superclass +initialize.)
            monitor_locker_t lock(classInitLock);
            if (!supercls  ||  supercls->isInitialized()) {
                _finishInitializing(cls, supercls);
            } else {
                _finishInitializingAfter(cls, supercls);
            }
        }
        return;
    }
    
    else if (cls->isInitializing()) {
        // We couldn't set INITIALIZING because INITIALIZING was already set.
        // If this thread set it earlier, continue normally.
        // If some other thread set it, block until initialize is done.
        // It's ok if INITIALIZING changes to INITIALIZED while we're here, 
        //   because we safely check for INITIALIZED inside the lock 
        //   before blocking.
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else {
            waitForInitializeToComplete(cls);
            return;
        }
    }
    
    else if (cls->isInitialized()) {
        // Set CLS_INITIALIZING failed because someone else already 
        //   initialized the class. Continue normally.
        // NOTE this check must come AFTER the ISINITIALIZING case.
        // Otherwise: Another thread is initializing this class. ISINITIALIZED 
        //   is false. Skip this clause. Then the other thread finishes 
        //   initialization and sets INITIALIZING=no and INITIALIZED=yes. 
        //   Skip the ISINITIALIZING clause. Die horribly.
        return;
    }
    
    else {
        // We shouldn't be here. 
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}
void _class_initialize(Class cls) 方法里面下面這段代碼就是為了遞歸保證先執(zhí)行父類的class_initialize method傲隶。
supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }

下面的代碼就是為了確認當本類沒有實現(xiàn)這個class_initialize method窃页,會再次調(diào)用父類的    
  if (PrintInitializing) {
            _objc_inform("INITIALIZE: calling +[%s initialize]",
                         cls->nameForLogging());
        }    
    

demo地址

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脖卖,一起剝皮案震驚了整個濱河市畦木,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛆封,老刑警劉巖勾栗,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件围俘,死亡現(xiàn)場離奇詭異,居然都是意外死亡界牡,警方通過查閱死者的電腦和手機宿亡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門她混,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泊碑,“玉大人,你說我怎么就攤上這事臭脓「购觯” “怎么了砚作?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵葫录,是天一觀的道長领猾。 經(jīng)常有香客問我,道長面粮,這世上最難降的妖魔是什么继低? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任袁翁,我火速辦了婚禮,結果婚禮上似枕,老公的妹妹穿的比我還像新娘年柠。我一直安慰自己,他們只是感情好答憔,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布虐拓。 她就那樣靜靜地躺著傲武,像睡著了一般。 火紅的嫁衣襯著肌膚如雪态兴。 梳的紋絲不亂的頭發(fā)上疟位,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音正勒,去河邊找鬼傻铣。 笑死,一個胖子當著我的面吹牛阱驾,可吹牛的內(nèi)容都是我干的怪蔑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼喧枷,長吁一口氣:“原來是場噩夢啊……” “哼弓坞!你這毒婦竟也來了渡冻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤帽借,失蹤者是張志新(化名)和其女友劉穎超歌,沒想到半個月后巍举,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蜓谋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年桃焕,在試婚紗的時候發(fā)現(xiàn)自己被綠了欠窒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片退子。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡型将,死狀恐怖荐虐,靈堂內(nèi)的尸體忽然破棺而出福扬,到底是詐尸還是另有隱情,我是刑警寧澤铛碑,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布汽烦,位于F島的核電站,受9級特大地震影響俗冻,放射性物質發(fā)生泄漏牍颈。R本人自食惡果不足惜煮岁,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望勤篮。 院中可真熱鬧色罚,春花似錦、人聲如沸金抡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禀晓。三九已至坝锰,卻和暖如春顷级,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弓颈。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工翔冀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纤子,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓跌捆,卻偏偏與公主長得像象颖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抄瓦,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容