iOS Category

一官觅、Category的實(shí)現(xiàn)原理
1、Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類的對(duì)象方法,類方法,屬性,協(xié)議信息.
2、在程序運(yùn)行的時(shí)候,runtime會(huì)將Category的數(shù)據(jù),合并到類信息中(類對(duì)象,元類信息中)

Category結(jié)構(gòu)體的定義:

struct category_t {

constchar*name;//類的名字(name)

classref_t cls;//類(cls)

struct method_list_t *instanceMethods; //category中所有給類添加的實(shí)例方法的列表(instanceMethods)

structmethod_list_t *classMethods;//category中所有添加的類方法的列表(classMethods)

structprotocol_list_t *protocols; //category實(shí)現(xiàn)的所有協(xié)議的列表(protocols)

structproperty_list_t *instanceProperties;//category中添加的所有屬性(instanceProperties)

};

從category的定義也可以看出category可以添加實(shí)例方法,類方法席里;可以遵守協(xié)議,添加屬性阵具;但無(wú)法添加實(shí)例變量碍遍。

添加方法列表的時(shí)候是后添加的在新形成的列表前部,這也是為什么在有多個(gè)category中有同名方法時(shí)阳液,后編譯的在調(diào)用時(shí)會(huì)“覆蓋”前面已編譯的方法怕敬。其實(shí)方法本身并沒(méi)有被覆蓋,只是調(diào)用的時(shí)候是從上而下查找方法列表帘皿。

   void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;

    if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        uint32_t newCount = oldCount + addedCount;
        array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
        newArray->count = newCount;
        array()->count = newCount;
        將以前的數(shù)組和新的分類數(shù)組合并
        for (int i = oldCount - 1; i >= 0; I--)
            newArray->lists[i + addedCount] = array()->lists[I]; 遍歷把以前的信息放入到新數(shù)組最后的位置
        for (unsigned i = 0; i < addedCount; i++)通過(guò)遍歷把新的信息按照遞增放入到新數(shù)組位置
            newArray->lists[i] = addedLists[I];  
        free(array());
        setArray(newArray);
        validate();
    }
    else if (!list  &&  addedCount == 1) {
        // 0 lists -> 1 list
        list = addedLists[0];
        validate();
    } 
    else {
        // 1 list -> many lists
        Ptr<List> oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;
        if (oldList) array()->lists[addedCount] = oldList;
        for (unsigned i = 0; i < addedCount; I++)
            array()->lists[i] = addedLists[I];
        validate();
    }
}

說(shuō)到Category方法的調(diào)用可以看看CompileSources文件放的位置

86CC2A34-76B5-40C7-9D6D-C3AE272FDE4F.png

這個(gè)時(shí)候日志打印對(duì)象方法 test方法LCPersonTest1
之所以是LCPersonTest1調(diào)用是因?yàn)樗鼤?huì)把后放入的位置放到分類數(shù)組的首位然后是Test2而我們的LCPerson是放在數(shù)組的最后位置 按照這個(gè)方
式可參考源碼

void  map_images_nolock(unsigned mhCount, const char * const mhPaths[],
              const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;

// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers. 
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
    preopt_init();
}

if (PrintImages) {
    _objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}


// Find all images with Objective-C metadata.
hCount = 0;

// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
    uint32_t i = mhCount;
    I-- 按照從大到小遍歷在編譯的過(guò)程中Test1是最后的
    while (i--) {
        const headerType *mhdr = (const headerType *)mhdrs[I];

        auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
        if (!hi) {
            // no objc data in this entry
            continue;
        }
        
        if (mhdr->filetype == MH_EXECUTE) {
            // Size some data structures based on main executable's size
#if __OBJC2__
            // If dyld3 optimized the main executable, then there shouldn't
            // be any selrefs needed in the dynamic map so we can just init
            // to a 0 sized map
            if ( !hi->hasPreoptimizedSelectors() ) {
              size_t count;
              _getObjc2SelectorRefs(hi, &count);
              selrefCount += count;
              _getObjc2MessageRefs(hi, &count);
              selrefCount += count;
            }
#else
            _getObjcSelectorRefs(hi, &selrefCount);
#endif
            
#if SUPPORT_GC_COMPAT
            // Halt if this is a GC app.
            if (shouldRejectGCApp(hi)) {
                _objc_fatal_with_reason
                    (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 
                     OS_REASON_FLAG_CONSISTENT_FAILURE, 
                     "Objective-C garbage collection " 
                     "is no longer supported.");
            }
#endif
        }
        
        hList[hCount++] = hi;  這個(gè)時(shí)候把Test1放到了首位东跪。然后Test2 最后Test 
        
        if (PrintImages) {
            _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", 
                         hi->fname(),
                         mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
                         hi->info()->isReplacement() ? " (replacement)" : "",
                         hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
                         hi->info()->optimizedByDyld()?" (preoptimized)":"");
        }
    }
}

// Perform one-time runtime initialization that must be deferred until 
// the executable itself is found. This needs to be done before 
// further initialization.
// (The executable may not be present in this infoList if the 
// executable does not contain Objective-C code but Objective-C 
// is dynamically loaded later.
if (firstTime) {
    sel_init(selrefCount);
    arr_init();

#if SUPPORT_GC_COMPAT
    // Reject any GC images linked to the main executable.
    // We already rejected the app itself above.
    // Images loaded after launch will be rejected by dyld.

    for (uint32_t i = 0; i < hCount; i++) {
        auto hi = hList[I];
        auto mh = hi->mhdr();
        if (mh->filetype != MH_EXECUTE  &&  shouldRejectGCImage(mh)) {
            _objc_fatal_with_reason
                (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 
                 OS_REASON_FLAG_CONSISTENT_FAILURE, 
                 "%s requires Objective-C garbage collection "
                 "which is no longer supported.", hi->fname());
        }
    }
#endif

#if TARGET_OS_OSX
    // Disable +initialize fork safety if the app is too old (< 10.13).
    // Disable +initialize fork safety if the app has a
    //   __DATA,__objc_fork_ok section.

    if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) {
        DisableInitializeForkSafety = true;
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: disabling +initialize fork "
                         "safety enforcement because the app is "
                         "too old.)");
        }
    }

    for (uint32_t i = 0; i < hCount; i++) {
        auto hi = hList[I];
        auto mh = hi->mhdr();
        if (mh->filetype != MH_EXECUTE) continue;
        unsigned long size;
        if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {
            DisableInitializeForkSafety = true;
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: disabling +initialize fork "
                             "safety enforcement because the app has "
                             "a __DATA,__objc_fork_ok section");
            }
        }
        break;  // assume only one MH_EXECUTE image
    }
#endif

}

if (hCount > 0) {
    _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}

firstTime = NO;

// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
    for (uint32_t i = 0; i < mhCount; i++) {
        func(mhdrs[I]);
    }
}
}

二、Category和class Extension的區(qū)別是什么?
class Extension在編譯的時(shí)候,它的數(shù)據(jù)就已經(jīng)包含在類信息中.
而Category在運(yùn)行時(shí)才會(huì)將數(shù)據(jù)合并到類信息中. 在編譯的時(shí)候,它有多少個(gè)分類就會(huì)生成多少個(gè)category_t.等到運(yùn)行的時(shí)候才會(huì)通過(guò)runtime將Category的數(shù)據(jù),合并到類信息中(類對(duì)象,元類信息中).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹰溜,一起剝皮案震驚了整個(gè)濱河市虽填,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌曹动,老刑警劉巖斋日,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異墓陈,居然都是意外死亡恶守,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門贡必,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)兔港,“玉大人,你說(shuō)我怎么就攤上這事仔拟∩婪” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵理逊,是天一觀的道長(zhǎng)橡伞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)晋被,這世上最難降的妖魔是什么兑徘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮羡洛,結(jié)果婚禮上挂脑,老公的妹妹穿的比我還像新娘藕漱。我一直安慰自己,他們只是感情好崭闲,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布肋联。 她就那樣靜靜地躺著,像睡著了一般刁俭。 火紅的嫁衣襯著肌膚如雪橄仍。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天牍戚,我揣著相機(jī)與錄音侮繁,去河邊找鬼。 笑死如孝,一個(gè)胖子當(dāng)著我的面吹牛宪哩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播第晰,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼锁孟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了茁瘦?” 一聲冷哼從身側(cè)響起品抽,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腹躁,沒(méi)想到半個(gè)月后桑包,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纺非,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年哑了,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烧颖。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弱左,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炕淮,到底是詐尸還是另有隱情拆火,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布涂圆,位于F島的核電站们镜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏润歉。R本人自食惡果不足惜模狭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望踩衩。 院中可真熱鬧嚼鹉,春花似錦贩汉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至线脚,卻和暖如春赐稽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浑侥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工又憨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锭吨。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像寒匙,于是被迫代替她去往敵國(guó)和親零如。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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