+load方法

  • +load方法會(huì)在程序初始化時(shí)候調(diào)用
  • 每個(gè)類和分類的+load方法都會(huì)調(diào)用饥悴,并且只調(diào)用一次
  • 調(diào)用順序
    1. 先調(diào)用類中的+load
    按照編譯順序調(diào)用缔恳,先編譯的先調(diào)用
    父類優(yōu)先調(diào)用。調(diào)用子類的+load方法之前會(huì)先調(diào)用父類的+load
    2. 后調(diào)用category中的+load
    按照編譯順序調(diào)用,先編譯的先調(diào)用

源碼分析

源碼版本objc4-781.tar.gz

入口:objc-os.mm
void _objc_init(void)
void load_images(const char *path __unused, const struct mach_header *mh)
void prepare_load_methods(const headerType *mhdr)
void call_load_methods(void)
static void call_class_loads(void)
static bool call_category_loads(void)

先看調(diào)用情況意荤,查看void call_load_methods(void)方法可知蹦玫,先調(diào)用了類的+load方法赎婚,再調(diào)用category中的+load方法。

/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first. 
* Category +load methods are not called until after the parent class's +load.
* 
* This method must be RE-ENTRANT, because a +load could trigger 
* more image mapping. In addition, the superclass-first ordering 
* must be preserved in the face of re-entrant calls. Therefore, 
* only the OUTERMOST call of this function will do anything, and 
* that call will handle all loadable classes, even those generated 
* while it was running.
*
* The sequence below preserves +load ordering in the face of 
* image loading during a +load, and make sure that no 
* +load method is forgotten because it was added during 
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren't any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
*    (a) there are more classes to load, OR
*    (b) there are some potential category +loads that have 
*        still never been attempted.
* Category +loads are only run once to ensure "parent class first" 
* ordering, even if a category +load triggers a new loadable class 
* and a new loadable category attached to that class. 
*
* Locking: loadMethodLock must be held by the caller 
*   All other locks must not be held.
**********************************************************************/
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;
}

static void call_class_loads(void) 調(diào)用類的+load樱溉。源碼中比較關(guān)鍵點(diǎn)是loadable_classes這個(gè)列表挣输,其中保存了類的+load方法,循環(huán)獲取到+load方法后直接調(diào)用福贞。所以了解了loadable_classes的初始化撩嚼,就知道了類中+load方法調(diào)用的順序。


/***********************************************************************
* call_class_loads
* Call all pending class +load methods.
* If new classes become loadable, +load is NOT called for them.
*
* Called only by call_load_methods().
**********************************************************************/
static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes; // load方法列表 
    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());
        }
        // 直接調(diào)用+load方法
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

static bool call_category_loads(void)方法中的調(diào)用情況類似,其中有個(gè)名為loadable_categories的列表完丽,循環(huán)獲取到category中的+load方法后直接調(diào)用恋技。所以了解了loadable_categories的初始化,就知道了category中+load方法調(diào)用的順序逻族。

/***********************************************************************
* call_category_loads
* Call some pending category +load methods.
* The parent class of the +load-implementing categories has all of 
*   its categories attached, in case some are lazily waiting for +initalize.
* Don't call +load unless the parent class is connected.
* If new categories become loadable, +load is NOT called, and they 
*   are added to the end of the loadable list, and we return TRUE.
* Return FALSE if no new categories became loadable.
*
* Called only by call_load_methods().
**********************************************************************/
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, @selector(load));
            cats[i].cat = nil;
        }
    }
    ... 以下省略 ...
    ... ...
    ... ...
}

void prepare_load_methods(const headerType *mhdr)該方法就是用來(lái)初始化loadable_classesloadable_categories的蜻底。

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

其中首先調(diào)用了static void schedule_class_load(Class cls)方法,加載類中的+load聘鳞。并且該方法中存在遞歸調(diào)用薄辅,在當(dāng)前的類的+load方法添加到數(shù)組之前,會(huì)先將父類作為參數(shù)遞歸調(diào)用該方法抠璃,如果父類沒(méi)有加載過(guò)站楚,就先加載父類。確保父類比子類先加載
void add_class_to_loadable_list(Class cls)中直接將類賦值給類數(shù)組鸡典,所以先加載的在數(shù)組的前面源请,這樣就可以確保父類的+load方法在子類之前。保證了后面的調(diào)用順序

/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed 
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
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); 
}
/***********************************************************************
* add_class_to_loadable_list
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

加載category中的+load方法則比較簡(jiǎn)單,調(diào)用void add_category_to_loadable_list(Category cat)彻况,直接按照編譯的順序谁尸,先編譯的在前面

/***********************************************************************
* add_category_to_loadable_list
* Category cat's parent class exists and the category has been attached
* to its class. Schedule this category for +load after its parent class
* becomes connected and has its own +load method called.
**********************************************************************/
void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = _category_getLoadMethod(cat);

    // Don't bother if cat has no +load method
    if (!method) return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市纽甘,隨后出現(xiàn)的幾起案子良蛮,更是在濱河造成了極大的恐慌,老刑警劉巖悍赢,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件决瞳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡左权,警方通過(guò)查閱死者的電腦和手機(jī)皮胡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赏迟,“玉大人屡贺,你說(shuō)我怎么就攤上這事⌒可保” “怎么了甩栈?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)糕再。 經(jīng)常有香客問(wèn)我量没,道長(zhǎng),這世上最難降的妖魔是什么突想? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任殴蹄,我火速辦了婚禮究抓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饶套。我一直安慰自己漩蟆,他們只是感情好垒探,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布妓蛮。 她就那樣靜靜地躺著,像睡著了一般圾叼。 火紅的嫁衣襯著肌膚如雪蛤克。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天夷蚊,我揣著相機(jī)與錄音构挤,去河邊找鬼。 笑死惕鼓,一個(gè)胖子當(dāng)著我的面吹牛筋现,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播箱歧,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼矾飞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呀邢?” 一聲冷哼從身側(cè)響起洒沦,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎价淌,沒(méi)想到半個(gè)月后申眼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝉衣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年括尸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片病毡。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡濒翻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剪验,到底是詐尸還是另有隱情肴焊,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布功戚,位于F島的核電站娶眷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啸臀。R本人自食惡果不足惜届宠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一烁落、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豌注,春花似錦伤塌、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至齿风,卻和暖如春药薯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背救斑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工童本, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脸候。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓穷娱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親运沦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泵额,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355