Category辨析

Category用于向已經(jīng)存在的類添加方法從而達到擴展已有類的目的巩梢,在很多情形下Category也是比創(chuàng)建子類更優(yōu)的選擇逸尖。Category用于大型類有效分解。新添加的方法會被被擴展的類的所有子類自動繼承煮仇。Category也可以用于替代這個已有類中某個方法的實體倘零,從而達到修復BUG的目的。如此就不能去調用已有類中原有的那個被替換掉方法實體了幻梯。需要注意的是兜畸,當準備有Category來替換某一個方法的時候,一定要保證實現(xiàn)原來方法的所有功能碘梢,否則這種替代就是沒有意義而且會引起新的BUG咬摇。

Category的方法不一定非要在@implementation中實現(xiàn),也可以在其他位置實現(xiàn)煞躬,但是當調用Category的方法時肛鹏,依據(jù)繼承樹沒有找到該方法的實現(xiàn),程序則會崩潰恩沛。Category理論上不能添加變量在扰,但是可以使用@dynamic 來彌補這種不足。

 @implementation NSObject (Category)
 @dynamic variable;
 - (id) variable
 {
 return objc_getAssociatedObject(self, externVariableKey);
 }
 - (void)setVariable:(id) variable
{
 objc_setAssociatedObject(self, externVariableKey, variable,
 OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

和子類不同的是雷客,Category不能用于向被擴展類添加實例變量芒珠。Category通常作為一種組織框架代碼的工具來使用。如果需要添加一個新的變量佛纫,則需添加子類妓局。如果只是添加一個新的方法,用Category是比較好的選擇呈宇。

runtime對category的加載過程

下面是runtime中category的結構:

struct _category_t {
const char *name; // 類的名字
struct _class_t *cls; // 要擴展的類對象好爬,編譯期間這個值是不會有的,在app被runtime加載時才會根據(jù)name對應到類對象
const struct _method_list_t *instance_methods; // 實例方法
const struct _method_list_t *class_methods; // 類方法
const struct _protocol_list_t *protocols; // 這個category實現(xiàn)的protocol甥啄,比較不常用在category里面實現(xiàn)協(xié)議存炮,但是確實支持的
const struct _prop_list_t *properties; // 這個category所有的property,這也是category里面可以定義屬性的原因蜈漓,不過這個property不會@synthesize實例變量穆桂,一般有需求添加實例變量屬性時會采用objc_setAssociatedObject和objc_getAssociatedObject方法綁定方法綁定,不過這種方法生成的與一個普通的實例變量完全是兩碼事融虽。
};

category動態(tài)擴展了原來類的方法享完,在調用者看來好像原來類本來就有這些方法似的,不論有沒有import category 的.h有额,都可以成功調用category的方法般又,都影響不到category的加載流程彼绷,import只是幫助了編譯檢查和鏈接過程。runtime加載完成后茴迁,category的原始信息在類結構里將不會存在寄悯。

objc runtime的加載入口是一個叫_objc_init的方法,在library加載前由libSystem dyld調用堕义,進行初始化操作猜旬。調用map_images方法將文件中的image map到內存。調用_read_images方法初始化map后的image倦卖,這里面干了很多的事情洒擦,像load所有的類、協(xié)議和category糖耸,著名的+ load方法就是這一步調用的秘遏。category的初始化丘薛,循環(huán)調用了_getObjc2CategoryList方法嘉竟。

在調用完_getObjc2CategoryList后,runtime終于開始了category的處理洋侨,首先分成兩撥舍扰,一撥是實例對象相關的調用addUnattachedCategoryForClass,一撥是類對象相關的調用addUnattachedCategoryForClass希坚,然后會調到attachCategoryMethods方法边苹,這個方法把一個類所有的category_list的所有方法取出來組成一個method_list_t ,這里是倒序添加的裁僧,也就是說个束,新生成的category的方法會先于舊的category的方法插入。

生成了所有method的list之后聊疲,調用attachMethodLists將所有方法前序添加進類的方法的數(shù)組中茬底,也就是說,如果原來類的方法是a,b,c获洲,類別的方法是1,2,3阱表,那么插入之后的方法將會是1,2,3,a,b,c,也就是說贡珊,原來類的方法被category的方法覆蓋了最爬,但被覆蓋的方法確實還在那里。

static void attachCategoryMethods(class_t *cls, category_list *cats,
                  BOOL *inoutVtablesAffected)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);

BOOL isMeta = isMetaClass(cls);
method_list_t **mlists = (method_list_t **)
    _malloc_internal(cats->count * sizeof(*mlists));

// Count backwards through cats to get newest categories first
int mcount = 0;
int i = cats->count;
BOOL fromBundle = NO;
while (i--) {
    method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
    if (mlist) {
        mlists[mcount++] = mlist;
        fromBundle |= cats->list[i].fromBundle;
    }
}

attachMethodLists(cls, mlists, mcount, NO, fromBundle, inoutVtablesAffected);

_free_internal(mlists);

}

這也即是我們上面說的Category修復Bug的原理门岔。

Extension

Extension非常像是沒有命名的類別爱致。擴展只是用來定義類的私有方法的,實現(xiàn)要在原始的.m里面寒随。還以用來改變原始屬性的一些性質糠悯。一般的時候胯努,Extension都是放在.m文件中@implementation的上方。 Extension中的方法必須在@implementation中實現(xiàn)逢防,否則編譯會報錯叶沛。Category沒有源代碼的類添加方法,格式:定義一對.h和.m忘朝。Extension作用于管理類的所有方法灰署,格式:把代碼寫到原始類的.m文件中。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末局嘁,一起剝皮案震驚了整個濱河市溉箕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悦昵,老刑警劉巖肴茄,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異但指,居然都是意外死亡寡痰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門棋凳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拦坠,“玉大人,你說我怎么就攤上這事剩岳≌瓯酰” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵拍棕,是天一觀的道長晓铆。 經(jīng)常有香客問我,道長绰播,這世上最難降的妖魔是什么骄噪? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮幅垮,結果婚禮上腰池,老公的妹妹穿的比我還像新娘。我一直安慰自己忙芒,他們只是感情好示弓,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呵萨,像睡著了一般奏属。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潮峦,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天囱皿,我揣著相機與錄音勇婴,去河邊找鬼。 笑死嘱腥,一個胖子當著我的面吹牛耕渴,可吹牛的內容都是我干的。 我是一名探鬼主播齿兔,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼橱脸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了分苇?” 一聲冷哼從身側響起添诉,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎医寿,沒想到半個月后栏赴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡靖秩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年须眷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盆偿。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡柒爸,死狀恐怖,靈堂內的尸體忽然破棺而出事扭,到底是詐尸還是另有隱情,我是刑警寧澤乐横,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布求橄,位于F島的核電站,受9級特大地震影響葡公,放射性物質發(fā)生泄漏罐农。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一催什、第九天 我趴在偏房一處隱蔽的房頂上張望涵亏。 院中可真熱鬧,春花似錦蒲凶、人聲如沸气筋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宠默。三九已至,卻和暖如春灵巧,著一層夾襖步出監(jiān)牢的瞬間搀矫,已是汗流浹背抹沪。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓤球,地道東北人融欧。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像卦羡,于是被迫代替她去往敵國和親蹬癌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結構(3).初始化時...
    歐辰_OSR閱讀 29,320評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,089評論 1 32
  • OC語言基礎 1.類與對象 類方法 OC的類方法只有2種:靜態(tài)方法和實例方法兩種 在OC中虹茶,只要方法聲明在@int...
    奇異果好補閱讀 4,250評論 0 11
  • 當?shù)貢r間凌晨四點醒來逝薪,國內已是清晨,萬物蘇醒蝴罪。在外旅行一直很少睡董济,似乎短短的睡眠已足夠得以補充。身體處在不自知的興...
    花瓣尖的舞蹈閱讀 882評論 1 1
  • 01 關于設備問題要门,有三種聲音虏肾,一種是來自設備方的,一種來自團隊內部的欢搜,一種來自剛剛聘請的技術顧問的封豪。每種聲音都能...
    劉小明攻城獅閱讀 262評論 0 0