重識iOS之Category

筆者最近梳理iOS知識脈絡(luò),計劃寫一個名為“重識iOS”的系列恬总,內(nèi)容來自平時的學習筆記,參考了一些文章和書籍肚邢,融入自己的理解以記錄壹堰。歡迎交流指正拭卿。本文為第一篇:Category。

category

介紹

熟悉設(shè)計模式的開發(fā)人員應(yīng)該都知道裝飾模式(Decorator)贱纠,它是在不修改原代碼的基礎(chǔ)上進行拓展峻厚。iOS開發(fā)中category就是對裝飾模式的典型實踐。

Category的簡介

Category是Objective-C 2.0之后添加的語言特性谆焊,category的主要作用是為已經(jīng)存在的類添加方法目木。

Category的結(jié)構(gòu)

我們知道Objective-C中類和對象都是C結(jié)構(gòu),category的結(jié)構(gòu)如下:

struct _category_t {
    const char *name; // 1
    struct _class_t *cls; // 2
    const struct _method_list_t *instance_methods; // 3
    const struct _method_list_t *class_methods; // 4
    const struct _protocol_list_t *protocols; // 5
    const struct _prop_list_t *properties; // 6
};
  1. name 主類的名字
  2. cls 要擴展的類對象懊渡,編譯期沒有值刽射,運行期根據(jù) name 對應(yīng)到類對象
  3. instance_methods 實例方法列表
  4. class_methods 類方法列表
  5. protocols 實現(xiàn)的協(xié)議列表,不常用但確實支持
  6. properties 屬性列表剃执,可以定義屬性誓禁,不能合成實例變量,可通過關(guān)聯(lián)對象進行綁定肾档,與傳統(tǒng)實例變量是兩樣東西摹恰。

使用場景

  • 為已經(jīng)存在的類添加方法(特別是為看不見源碼的系統(tǒng)類)
  • 把類的實現(xiàn)分開在幾個不同的文件中,好處:
  1. 減少單個文件體積
  2. 功能分離
  3. 多人共同開發(fā)一個類
  4. 實現(xiàn)按需加載

特點(局限性)

  • 不能添加實例變量(可通過runtime關(guān)聯(lián)對象)

注意:category可以添加屬性怒见,只是不能自動合成實例變量俗慈。

  • 方法覆蓋:與主類同名的方法優(yōu)先級高于主類方法

注意:category并不是完全替換掉主類的同名方法,只是類的方法列表里會出現(xiàn)兩個名字一樣的方法遣耍,并且category的方法會排在本類方法的前面闺阱,運行時查找方法按照順序,一旦找到就停止舵变,也就出現(xiàn)了所謂的方法覆蓋酣溃。

Category與Extension

  • Category在 運行期決議 。category無法添加實例變量纪隙,因為在運行期對象的內(nèi)存布局已經(jīng)確定赊豌,添加實例變量會破壞類的內(nèi)部結(jié)構(gòu)。

  • Extension在 編譯器決議 绵咱。extension可以單獨創(chuàng)建生成 .h 文件碘饼,常寫在主類 .m 中作為類的一部分,一般用來隱藏類的私有信息悲伶,必須有一個類的源碼才能為其添加一個extension艾恼。

關(guān)聯(lián)對象

我們已經(jīng)知道category不能自動合成實例變量,但是可以通過runtime關(guān)聯(lián)對象的方式來實現(xiàn) setter 拢切、getter蒂萎。

//.h文件
#import "MyClass.h"

@interface MyClass (Addition)

@property (nonatomic, copy) NSString *name;

@end


//.m文件
#import "MyClass+Addition.h"

static void *kNameKey = &kNameKey;

@implementation MyClass (Addition)

/** 
* 設(shè)置關(guān)聯(lián)對象
* 
* @param 需要被關(guān)聯(lián)的對象
* @param key 關(guān)聯(lián)對象的key 一般這樣設(shè)置static char key;
* @param value 被關(guān)聯(lián)對象的屬性,如果設(shè)置nil淮椰,就取消關(guān)聯(lián)
* @param policy 關(guān)聯(lián)策略,相當于屬性的內(nèi)存管理語義
*/

- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, kNameKey, name, OBJC_ASSOCIATION_COPY);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, kNameKey);
}

@end


//objc_setAssociatedObject第4個參數(shù):策略的枚舉
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           //關(guān)聯(lián)對象的屬性是弱引用
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //關(guān)聯(lián)對象的屬性是強引用且關(guān)聯(lián)對象不使用原子性
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   //關(guān)聯(lián)對象的屬性是copy且關(guān)聯(lián)對象不使用原子性
    OBJC_ASSOCIATION_RETAIN = 01401,       //關(guān)聯(lián)對象的屬性是copy且關(guān)聯(lián)對象使用原子性
    OBJC_ASSOCIATION_COPY = 01403          //關(guān)聯(lián)對象的屬性是copy且關(guān)聯(lián)對象使用原子性
};

延伸

  • 1.在類的 +load 方法中可以調(diào)用category里聲明的方法嗎?

可以主穗,因為附加category到類的工作會先于 +load 方法的執(zhí)行泻拦。

  • 2.類和category的 +load 方法調(diào)用順序是什么樣的?

+load 的執(zhí)行順序是:先類忽媒,后category争拐。而各個category的 +load執(zhí)行順序是由編譯順序決定的。

  • 3.關(guān)聯(lián)對象存在哪晦雨?如何存儲架曹?對象銷毀時候如何處理關(guān)聯(lián)對象?

所有的關(guān)聯(lián)對象都由 AssociationsManager 管理闹瞧, AssociationsManager 里面是由一個靜態(tài) AssociationsHashMap 來存儲所有的關(guān)聯(lián)對象的绑雄。

相當于把所有對象的關(guān)聯(lián)對象都存在一個全局 map 面。而 mapkey 是這個對象的指針地址奥邮, value 一個 AssociationsHashMap 万牺,里面保存了關(guān)聯(lián)對象的鍵值對。

runtime的銷毀對象函數(shù) objc_destructInstance 里面會判斷這個對象有沒有關(guān)聯(lián)對象洽腺,如果有脚粟,會調(diào)用 _object_remove_assocations 做關(guān)聯(lián)對象的清理工作。

參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蘸朋,一起剝皮案震驚了整個濱河市核无,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藕坯,老刑警劉巖厕宗,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異堕担,居然都是意外死亡已慢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門霹购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佑惠,“玉大人,你說我怎么就攤上這事齐疙∧た” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵贞奋,是天一觀的道長赌厅。 經(jīng)常有香客問我,道長轿塔,這世上最難降的妖魔是什么特愿? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任仲墨,我火速辦了婚禮,結(jié)果婚禮上揍障,老公的妹妹穿的比我還像新娘目养。我一直安慰自己,他們只是感情好毒嫡,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布癌蚁。 她就那樣靜靜地躺著,像睡著了一般兜畸。 火紅的嫁衣襯著肌膚如雪努释。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天咬摇,我揣著相機與錄音伐蒂,去河邊找鬼。 笑死菲嘴,一個胖子當著我的面吹牛饿自,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播龄坪,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昭雌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了健田?” 一聲冷哼從身側(cè)響起烛卧,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妓局,沒想到半個月后总放,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡好爬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年局雄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片存炮。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡炬搭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出穆桂,到底是詐尸還是另有隱情宫盔,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布享完,位于F島的核電站灼芭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏般又。R本人自食惡果不足惜彼绷,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一巍佑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苛预,春花似錦句狼、人聲如沸笋熬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胳螟。三九已至昔馋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間糖耸,已是汗流浹背秘遏。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘉竟,地道東北人邦危。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像舍扰,于是被迫代替她去往敵國和親倦蚪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

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