【iOS面試糧食】OC語言—Category(分類)和類擴(kuò)展(extension)截珍、關(guān)聯(lián)對象

本文章將記錄有關(guān) Category(分類)和類擴(kuò)展(extension)、關(guān)聯(lián)對象的特性箩朴,如有錯誤歡迎指出~

Category(分類)

分類的應(yīng)用在App的開發(fā)中是非常廣泛的,它可以動態(tài)地為已有類添加新行為炸庞。

我們平常都是使用分類來對系統(tǒng)的類封裝一些小功能,如NSString判空處理等查牌,可以看下 ibireme大神開源的這個庫YYCategories,都是針對系統(tǒng)的類使用分類拓展的小功能纸颜,很實(shí)用。再來看看業(yè)界聞名的空白頁框架DZNEmptyDataSet绎橘,它就是通過對 UIScrollView使用分類功能,非常完美称鳞、無侵入的解決了無數(shù)據(jù)時涮较,避免白屏的尷尬胡岔,改善用戶體驗(yàn)。真的是居家旅行必備之利器靶瘸,強(qiáng)大~

隨著業(yè)務(wù)的增長,.m文件會越來越膨脹怨咪,膨脹到你開始撓頭屋剑。我們也可以利用分類的特性诗眨,將類的實(shí)現(xiàn)分開在幾個不同的文件里面,好處顯而易見:

  • 減少單個文件的體積
  • 把不同的功能分配到不同的分類里,便于管理
  • 可以按需加載想要的分類

通常巍膘,我會添加一些 AppDelegate的分類,比如AppDelegate+Services,專門用來初始化各種第三方等等峡懈。

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

Objective-C中璃饱,所有的類和對象在runtime層都是用struct表示的肪康,category也是如此,它的結(jié)構(gòu)體為category_t

typedef struct category_t {
    const char *name;                               //  類名
    classref_t cls;                                 //  類
    struct method_list_t *instanceMethods;          //  所有給類添加的實(shí)例方法的列表
    struct method_list_t *classMethods;             //  所有添加的類方法的列表
    struct protocol_list_t *protocols;              //  實(shí)現(xiàn)的所有協(xié)議的列表
    struct property_list_t *instanceProperties;     //  添加的所有屬性
} category_t;

從結(jié)構(gòu)體可以看出磷支,分類能

  • 給類添加實(shí)例方法 (instanceMethod)
  • 給類添加類方法 (classMethod)
  • 實(shí)現(xiàn)協(xié)議 (protocol)
  • 添加屬性 (instancePropertie)

但是不能添加實(shí)例變量,即無法自動生成實(shí)例變量的setter和getter方法雾狈。當(dāng)然,我們可以通過關(guān)聯(lián)對象來實(shí)現(xiàn)分類對實(shí)例變量的添加箍邮,請看后面章節(jié)~

Category的方法會“覆蓋”掉原來類的同名方法茉帅?

這個問題的分析锭弊,可以看下美圖技術(shù)團(tuán)隊(duì)的這篇文章擂错,很詳細(xì)。這里只記錄一下總結(jié):

  • Category的方法沒有“完全替換掉”原來類已經(jīng)有的方法钮呀,也就是說如果Category和原來類都有methodA剑鞍,那么Category附加完成之后爽醋,類的方法列表里會有兩個methodA

  • Category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的后面蚂四,這也就是我們平常所說的Category的方法會“覆蓋”掉原來類的同名方法,這是因?yàn)檫\(yùn)行時在查找方法的時候是順著方法列表的順序查找的遂赠,它只要一找到對應(yīng)名字的方法久妆,很開心的返回了跷睦,不會在理會后面的同名方法。

  • 同名方法的調(diào)用抑诸,是根據(jù)編譯順序決定的烂琴,對于“覆蓋”掉的方法,會先找到最后一個編譯的category里的對應(yīng)方法奸绷。可查看項(xiàng)目的 Build Phases -> Compile Sources健盒,位置越往后绒瘦,越晚編譯扣癣。

類擴(kuò)展(extension)

extension的別名有很多,擴(kuò)展父虑、延展该酗、匿名分類士嚎。它就是類的一部分,在編譯期和頭文件里的@interface以及實(shí)現(xiàn)文件里的@implement一起形成一個完整的類莱衩,它伴隨類的產(chǎn)生而產(chǎn)生,亦隨之一起消亡笨蚁。在viewController.m文件中它長這個樣子:

@interface ViewController ()
@end

是不是特別的熟悉~

沒錯睹晒,它看起來很像一個匿名的category括细。我們一般用來聲明私有方法伪很,私有屬性和私有成員變量奋单。

extension的應(yīng)用很簡單, 我們基本天天都在用览濒。需要注意的點(diǎn)是它和category的區(qū)別:

  • extension 在編譯期決議, category在運(yùn)行期決議匾七。

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

如上所述絮短,category是無法為分類添加實(shí)例變量的昨忆,但是在實(shí)際開發(fā)中往往需要在分類中添加和對象關(guān)聯(lián)的值。這時我們可以利用runtime的特性,通過關(guān)聯(lián)對象來實(shí)現(xiàn)為分類添加實(shí)例變量的功能席里。

關(guān)聯(lián)對象是指某個對象通過一個唯一的key連接到一個類的實(shí)例上。

看下runtime提供給我們的3個API方法:

// 關(guān)聯(lián)對象奖磁,傳入 nil 則可以移除已有的關(guān)聯(lián)對象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  
// 獲取關(guān)聯(lián)的對象
id objc_getAssociatedObject(id object, const void *key)
  
/** 移除關(guān)聯(lián)的對象。這個方法會移除一個對象的所有關(guān)聯(lián)對象咖为,將該對象恢復(fù)成“原始”狀態(tài)秕狰。
    這樣做就很有可能把別人添加的關(guān)聯(lián)對象也一并移除躁染,所以我們通常用不上這個方法。
    一般的做法是通過給 objc_setAssociatedObject 方法傳入 nil 來移除某個已有的關(guān)聯(lián)對象吞彤。*/
void objc_removeAssociatedObjects(id object)

參數(shù)說明:

  • id object:被關(guān)聯(lián)的對象
  • const void *key:關(guān)聯(lián)的key我衬,要求唯一饰恕。一般用 selector ,使用 getter 方法的名稱作為 key 值埋嵌。
  • id value:關(guān)聯(lián)的對象
  • objc_AssociationPolicy policy:內(nèi)存管理的策略

objc_AssociationPolicy的枚舉值和相關(guān)說明:

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,            // 指定一個弱引用相關(guān)聯(lián)的對象
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,  // 指定相關(guān)對象的強(qiáng)引用,非原子性
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,    // 指定相關(guān)的對象被復(fù)制莉恼,非原子性
    OBJC_ASSOCIATION_RETAIN = 01401,        // 指定相關(guān)對象的強(qiáng)引用,原子性
    OBJC_ASSOCIATION_COPY = 01403           // 指定相關(guān)的對象被復(fù)制俐银,原子性   
};

在絕大多數(shù)情況下,我們都會使用 OBJC_ASSOCIATION_RETAIN_NONATOMIC 的關(guān)聯(lián)策略端仰,這可以保證我們持有關(guān)聯(lián)對象。

看一下簡單的應(yīng)用:

ViewController的分類 ViewController+AssociatedObjects添加一個字符串類型的成員變量荔烧。

@interface ViewController (AssociatedObjects)

@property (strong, nonatomic) NSString *associatedObject;

@end

@implementation ViewController (AssociatedObjects)


- (NSString *)associatedObject {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setassociatedObject:(NSString *)associatedObject {
    objc_setAssociatedObject(self, @selector(associatedObject), associatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

如果想了解一下關(guān)聯(lián)對象底層的實(shí)現(xiàn)原理,可以看下雷純峰大神Objective-C Associated Objects 的實(shí)現(xiàn)原理鹤竭,這里只記錄一些總結(jié):

Q : 關(guān)聯(lián)對象被存儲在什么地方,是不是存放在被關(guān)聯(lián)對象本身的內(nèi)存中臀稚?

A : 關(guān)聯(lián)對象與被關(guān)聯(lián)對象本身的存儲并沒有直接的關(guān)系,它是存儲在單獨(dú)的哈希表中的。所有的關(guān)聯(lián)對象都由AssociationsManager管理窜管,它是由一個靜態(tài)AssociationsHashMap來存儲所有的關(guān)聯(lián)對象的。這相當(dāng)于把所有對象的關(guān)聯(lián)對象都存在一個全局map里面幕帆。而map的的key是這個對象的指針地址(任意兩個不同對象的指針地址一定是不同的)获搏,而這個map的value又是另外一個AssociationsHashMap失乾,里面保存了關(guān)聯(lián)對象的key對。

Q : 關(guān)聯(lián)對象的生命周期是怎樣的碱茁,什么時候被釋放裸卫,什么時候被移除早芭?

A : 關(guān)聯(lián)對象的釋放時機(jī)與移除時機(jī)并不總是一致。

參考

深入理解Objective-C:Category

Objective-C Associated Objects 的實(shí)現(xiàn)原理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末退个,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子语盈,更是在濱河造成了極大的恐慌,老刑警劉巖刀荒,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缠借,居然都是意外死亡干毅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門硝逢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渠鸽,你說我怎么就攤上這事〔窆蓿” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵革屠,是天一觀的道長排宰。 經(jīng)常有香客問我,道長红省,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任吧恃,我火速辦了婚禮,結(jié)果婚禮上痕寓,老公的妹妹穿的比我還像新娘傲醉。我一直安慰自己呻率,他們只是感情好硬毕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布礼仗。 她就那樣靜靜地躺著,像睡著了一般元践。 火紅的嫁衣襯著肌膚如雪韭脊。 梳的紋絲不亂的頭發(fā)上单旁,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音象浑,去河邊找鬼。 笑死愉豺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚪拦。 我是一名探鬼主播越除,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼外盯,長吁一口氣:“原來是場噩夢啊……” “哼翼雀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狼渊,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤类垦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后城须,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糕伐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了良瞧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡挚冤,死狀恐怖赞庶,靈堂內(nèi)的尸體忽然破棺而出歧强,到底是詐尸還是另有隱情,我是刑警寧澤誊锭,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站丧靡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏温治。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一熬荆、第九天 我趴在偏房一處隱蔽的房頂上張望舟山。 院中可真熱鬧卤恳,春花似錦、人聲如沸突琳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蠢琳。三九已至,卻和暖如春傲须,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泰讽。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工例衍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇绵。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓肄渗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親咬最。 傳聞我的和親對象是個殘疾皇子翎嫡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

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