Load和initialize方法

1府树、load方法

load方法會(huì)在runtime加載類、分類時(shí)調(diào)用(在main函數(shù)開始之前),與這個(gè)類是否用到無關(guān)仇参。

類、分類中的load方法

蘋果源碼網(wǎng)站下載下來runtime的objc4-723源碼婆殿。
obje-runtime-new.mm文件中有個(gè)load_images方法指明加載文件到內(nèi)存中诈乒,其中call_load_methods這句代碼便對(duì)應(yīng)load方法的處理:

  • 這里可以看出是先調(diào)用所有類文件里的load方法,再調(diào)用所有Category文件里的load方法
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) {
            //首先調(diào)用類的load方法
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        //然后調(diào)用分類的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;
}

再看call_class_loads方法里的邏輯:

  • 這里可以看出是找到每個(gè)class中l(wèi)oad方法存放的內(nèi)存地址婆芦,然后通過內(nèi)存地址來調(diào)用load方法的
static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    //循環(huán)處理所有的類文件里的load方法
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        //這個(gè)load_method是個(gè)指針類型怕磨,里面存放的是load方法的內(nèi)存地址
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        //此處直接通過load方法的內(nèi)存地址來調(diào)用load方法
        (*load_method)(cls, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

再看call_category_loads方法里的邏輯:

  • 同樣,調(diào)用Category中的load方法也是直接通過內(nèi)存地址的方法調(diào)用消约,此處便不再作注解
static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }

    // Compact detached list (order-preserving)
    shift = 0;
    for (i = 0; i < used; i++) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift++;
        }
    }
    used -= shift;

    // Copy any new +load candidates from the new list to the detached list.
    new_categories_added = (loadable_categories_used > 0);
    for (i = 0; i < loadable_categories_used; i++) {
        if (used == allocated) {
            allocated = allocated*2 + 16;
            cats = (struct loadable_category *)
                realloc(cats, allocated *
                                  sizeof(struct loadable_category));
        }
        cats[used++] = loadable_categories[i];
    }

    // Destroy the new list.
    if (loadable_categories) free(loadable_categories);

    // Reattach the (now augmented) detached list. 
    // But if there's nothing left to load, destroy the list.
    if (used) {
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        if (cats) free(cats);
        loadable_categories = nil;
        loadable_categories_used = 0;
        loadable_categories_allocated = 0;
    }

    if (PrintLoading) {
        if (loadable_categories_used != 0) {
            _objc_inform("LOAD: %d categories still waiting for +load\n",
                         loadable_categories_used);
        }
    }

    return new_categories_added;
}

之前肠鲫,我們?cè)诳碈ategory的時(shí)候,當(dāng)類文件和分類文件中寫了同名方法時(shí)或粮,我們調(diào)用方法后导饲,分析得出只調(diào)用了分類中的方法(多個(gè)分類只調(diào)用一個(gè))。這是因?yàn)檎{(diào)用方法時(shí)利用的是objc_sendMsg的機(jī)制其調(diào)用函數(shù)的氯材。
現(xiàn)在明顯可以看出load方法和其他方法的不同渣锦,調(diào)用load方法本就在runtime加載時(shí),是通過找到load方法存儲(chǔ)的內(nèi)存地址調(diào)用的氢哮,所以每個(gè)class的load方法都會(huì)被調(diào)用袋毙。

父類、子類中的load方法

還是來看一下源碼:
obje-runtime-new.mm文件中有個(gè)prepare_load_methods方法命浴,在這里面schedule_class_load這句代碼對(duì)應(yīng)的是處理load方法時(shí)對(duì)class文件的加載順序處理:

  • 可以看出在處理自己的之前娄猫,會(huì)先處理自己的父類
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    //在處理自己之前, 遞歸調(diào)用,傳入的參數(shù)是自己的父類
    schedule_class_load(cls->superclass);

    //將cls(類)添加到loadable_classes數(shù)組中(加入的新class在數(shù)組后面)
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
load方法的總結(jié)

load方法會(huì)在runtime加載類贱除、分類時(shí)調(diào)用
每個(gè)類生闲、分類的+load,在程序運(yùn)行過程中只調(diào)用一次

  • 調(diào)用順序
    先調(diào)用類的+load(先編譯月幌,先調(diào)用)
    調(diào)用子類的+load之前會(huì)先調(diào)用父類的+load
    再調(diào)用分類的+load(先編譯碍讯,先調(diào)用)

也就是說:
1.當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
2.當(dāng)子類未實(shí)現(xiàn)load方法時(shí),不會(huì)調(diào)用父類load方法
3.類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
4.當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(取決于分類文件的編譯順序)

2、initialize

initialize方法是在類第一次接受到消息時(shí)調(diào)用扯躺,必須是類使用到了才會(huì)被調(diào)用捉兴。它是通過發(fā)送objc_sendMsg的機(jī)制的方式去調(diào)用的。

類录语、分類中的initialize方法

在學(xué)習(xí)category的時(shí)候倍啥,我們已經(jīng)知道如果是使用objc_sendMsg的機(jī)制去調(diào)用方法的話,都是會(huì)先調(diào)用分類的同名方法澎埠,所以如果分類實(shí)現(xiàn)了+initialize方法虽缕,就覆蓋類本身的+initialize方法。

父類蒲稳、子類中的initialize方法

如果子類沒有實(shí)現(xiàn)+initialize氮趋,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次)伍派。

因?yàn)樵创a太過分散,我就不貼源碼了剩胁,采用偽代碼的方法簡(jiǎn)單表示一下:

//以上說明是建立在class本身或者class的分類實(shí)現(xiàn)了initialize方法的基礎(chǔ)上诉植。
//如果class本身沒有實(shí)現(xiàn)initialize方法,且其分類中也沒有實(shí)現(xiàn)initialize方法昵观,
//那么就會(huì)沿著其父類向上尋找晾腔,一直找到實(shí)現(xiàn)了initialize方法的父類,調(diào)用該父類的initialize方法

if(class本身沒有被初始化){
    if(class有父類 && 父類沒有被初始化){
        if(class的父類有分類 && 分類中實(shí)現(xiàn)了initialize方法){
            調(diào)用class的父類的分類中的initialize方法
        }else{
            調(diào)用class的父類中的initialize方法
        }
    }else{
        if(class有分類 && 分類中實(shí)現(xiàn)了initialize方法){
            調(diào)用class的分類中的initialize方法
        }else{
            調(diào)用class本身的initialize方法
        }
    }
}
initialize方法的總結(jié)

+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用

  • 調(diào)用順序
    先調(diào)用父類的+initialize啊犬,再調(diào)用子類的+initialize(先初始化父類建车,再初始化子類,每個(gè)類只會(huì)初始化1次)
    類和分類中都含有+initialize時(shí)椒惨,調(diào)用分類中的+initialize方法

也就是說:
1.父類的initialize方法會(huì)比子類先執(zhí)行
2.當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會(huì)調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會(huì)覆蓋父類initialize方法缤至。
3.當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行最后一個(gè)(取決于分類文件的編譯順序)

3、總結(jié)

<1>Category中有l(wèi)oad方法嗎康谆?load方法是什么時(shí)候調(diào)用的领斥?load 方法能繼承嗎?
有l(wèi)oad方法
load方法在runtime加載類沃暗、分類的時(shí)候調(diào)用
load方法不遵守繼承規(guī)則月洛,一般情況下不會(huì)主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自動(dòng)調(diào)用

<2>load孽锥、initialize方法的區(qū)別什么嚼黔?

  • 調(diào)用方式
    load是根據(jù)函數(shù)地址直接調(diào)用的
    initizlize是通過objc_sendMsg調(diào)用
  • 調(diào)用時(shí)刻
    load是runtime加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用一次)
    initizlize是類第一次接收到消息的時(shí)候調(diào)用惜辑,每一個(gè)類只會(huì)initizlize一次(父類的initizlize方法可能會(huì)被調(diào)用多次唬涧,但是這是因?yàn)樽宇悰]有實(shí)現(xiàn)initizlize方法,objc_sendMsg方法通過isa找到父類的同名方法)

<3>load盛撑、initialize方法在category中的調(diào)用的順序碎节?以及出現(xiàn)繼承時(shí)他們之間的調(diào)用過程?

  • load
    1>先調(diào)用類的load
    ? a)先編譯的類優(yōu)先調(diào)用load方法
    ? b)調(diào)用子類的load方法之前抵卫,會(huì)先調(diào)用父類的load方法
    2>再去調(diào)用分類的load
    ? a)先編譯的分類狮荔,優(yōu)先調(diào)用load
  • initizlize
    1>先初始化父類
    ? a)有分類中實(shí)現(xiàn)了initizlize,調(diào)用先編譯的分類的initizlize方法
    2>再初始化子類(子類如果沒有實(shí)現(xiàn)initizlize介粘,會(huì)調(diào)用到父類的initizlize方法)
    ? a)有分類中實(shí)現(xiàn)了initizlize殖氏,調(diào)用先編譯的分類的initizlize方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市姻采,隨后出現(xiàn)的幾起案子雅采,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件总滩,死亡現(xiàn)場(chǎng)離奇詭異纲堵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)闰渔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門席函,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冈涧,你說我怎么就攤上這事茂附。” “怎么了督弓?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵营曼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我愚隧,道長(zhǎng)蒂阱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任狂塘,我火速辦了婚禮录煤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荞胡。我一直安慰自己妈踊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布泪漂。 她就那樣靜靜地躺著廊营,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萝勤。 梳的紋絲不亂的頭發(fā)上露筒,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音纵刘,去河邊找鬼邀窃。 笑死,一個(gè)胖子當(dāng)著我的面吹牛假哎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鞍历,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼舵抹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了劣砍?” 一聲冷哼從身側(cè)響起惧蛹,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后香嗓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迅腔,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年靠娱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沧烈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡像云,死狀恐怖锌雀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迅诬,我是刑警寧澤腋逆,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站侈贷,受9級(jí)特大地震影響惩歉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俏蛮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一柬泽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嫁蛇,春花似錦锨并、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抑党,卻和暖如春包警,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背底靠。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工害晦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暑中。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓壹瘟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親鳄逾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稻轨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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