OC中的load方法

Category的load方法什么時候調用医瘫?load方法能繼承嗎

  1. 在runtime加載類和分類的時候調用。類的load方法會先調用,然后才會按照編譯時的順序調用分類的load方法。
  2. 每個類钞诡、分類的+load方法,在程序運行過程中只調用一次
  3. 能繼承,繼承就是OC的消息發(fā)送機制荧降,通過isa指針接箫,再通過superclasss指針查找父類的方法。但一般不會主動調用+load方法朵诫,都是系統(tǒng)加載類的時候自動調用的

類和分類的+load方法的調用順序

  1. 先調用類的+load方法列牺,再調用分類的+load方法
  2. 類的+load方法會按照編譯順序調用,在調用+load方法的過程中拗窃,如果該類有父類瞎领,就會先調用該類父類的+load方法
  3. 分類的+load方法按照編譯順序進行調用
    注:通過下面的源碼分析,根據(jù)prepare_load_methodsschedule_class_load可以得到上面的調用順序

Category load 方法源碼分析:

objc4-781版源碼

注意:+load方法是根據(jù)方法地址直接調用随夸,并不是進過objc_msgSend函數(shù)調用

源碼解讀過程:objc-os.mm 文件開始

  1. _objc_init 方法
  2. load_images : 加載鏡像(模塊)九默,其中會調用 call_load_methods();
    3.call_load_methods(): 會先調用 call_class_loads()方法,再調用call_category_loads()方法宾毒。所以會先調用類的+load方法
  3. prepare_load_methods :準備所有的load方法驼修,會根據(jù)_getObjc2NonlazyClassList這個數(shù)組,去循環(huán)調用schedule_class_load方法诈铛。所以_getObjc2NonlazyClassList順序乙各,就決定了類load方法的調用順序。而_getObjc2NonlazyClassList順序就直接跟編譯順序相關幢竹。
  • schedule_class_load:安排配置load方法耳峦,該函數(shù)會遞歸調用 schedule_class_load(cls->superclass);并把父類對象傳進入。所以父類對象會先添加到數(shù)組中焕毫。所以父類的load會先調用蹲坷,再調用子類的load方法
  • add_class_to_loadable_list:將class添加到數(shù)組中,
  • add_category_to_loadable_list:將category添加到數(shù)組中
  1. call_load_methods:調用load方法
  • call_class_loads:調用所有類的load方法
  • call_category_loads:調用所有分類的load方法
  • (*load_method)(cls,SEL_load):通過地址直接調用方法

先關源碼:

// 該結構專門用來加載類邑飒,method 存放的方法就是類的load方法
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};
// 該結構專門用來加載分類循签,method 存放的方法就是分類類的load方法
struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};
load_images
void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
        didInitialAttachCategories = true;
        loadAllCategories();
    }

    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}
prepare_load_methods

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *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
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}
schedule_class_load
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
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
call_load_methods
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) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        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
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.
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        // 獲取load方法的地址
        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方法
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}
call_category_loads
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方法地址
        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, @selector(load));
            cats[i].cat = nil;
        }
    }
....
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末县匠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子撒轮,更是在濱河造成了極大的恐慌乞旦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腔召,死亡現(xiàn)場離奇詭異杆查,居然都是意外死亡扮惦,警方通過查閱死者的電腦和手機臀蛛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人浊仆,你說我怎么就攤上這事客峭。” “怎么了抡柿?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵舔琅,是天一觀的道長。 經(jīng)常有香客問我洲劣,道長备蚓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任囱稽,我火速辦了婚禮郊尝,結果婚禮上,老公的妹妹穿的比我還像新娘战惊。我一直安慰自己流昏,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布吞获。 她就那樣靜靜地躺著况凉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪各拷。 梳的紋絲不亂的頭發(fā)上刁绒,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機與錄音烤黍,去河邊找鬼膛锭。 笑死,一個胖子當著我的面吹牛蚊荣,可吹牛的內容都是我干的初狰。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼互例,長吁一口氣:“原來是場噩夢啊……” “哼奢入!你這毒婦竟也來了?” 一聲冷哼從身側響起媳叨,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤腥光,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后糊秆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體武福,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年痘番,在試婚紗的時候發(fā)現(xiàn)自己被綠了捉片。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片平痰。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伍纫,靈堂內的尸體忽然破棺而出宗雇,到底是詐尸還是另有隱情,我是刑警寧澤莹规,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布赔蒲,位于F島的核電站,受9級特大地震影響良漱,放射性物質發(fā)生泄漏舞虱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一母市、第九天 我趴在偏房一處隱蔽的房頂上張望砾嫉。 院中可真熱鬧,春花似錦窒篱、人聲如沸焕刮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽配并。三九已至,卻和暖如春高镐,著一層夾襖步出監(jiān)牢的瞬間溉旋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工嫉髓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留观腊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓算行,卻偏偏與公主長得像梧油,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子州邢,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351