關(guān)于Category,Extension的一些理解

(一)Category

特點(diǎn):

category只能給某個(gè)已有的類(lèi)擴(kuò)充方法,不能擴(kuò)充成員變量。

category中也可以添加屬性,只不過(guò)@property只會(huì)生成setter和getter的聲明,不會(huì)生成setter和getter的實(shí)現(xiàn)以及成員變量。

如果category中的方法和類(lèi)中原有方法同名,運(yùn)行時(shí)會(huì)優(yōu)先調(diào)用category中的方法。也就是,category中的方法會(huì)覆蓋掉類(lèi)中原有的方法。所以開(kāi)發(fā)中盡量保證不要讓分類(lèi)中的方法和原有類(lèi)中的方法名相同。避免出現(xiàn)這種情況的解決方案是給分類(lèi)的方法名統(tǒng)一添加前綴。比如category_。

如果多個(gè)category中存在同名的方法,運(yùn)行時(shí)到底調(diào)用哪個(gè)方法由編譯器決定,最后一個(gè)參與編譯的方法會(huì)被調(diào)用。這個(gè)要看在xcode配置中,他們的.m 是在前還是在后,放在上面的先編譯,那么后面編譯的category同名方法就會(huì)被調(diào)用。

結(jié)構(gòu):

Category不允許為已有的類(lèi)添加新的成員變量,實(shí)際上允許添加屬性的,同樣可以使用@property,但是不會(huì)生成_變量(帶下劃線的成員變量),也不會(huì)生成添加屬性的getter和setter方法,所以,盡管添加了屬性,也無(wú)法使用點(diǎn)語(yǔ)法調(diào)用getter和setter方法。

我們看下category的結(jié)構(gòu):

typedef?struct?category_t?{

const?char?*name;??//類(lèi)的名字

classref_t?cls;??//類(lèi)

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

struct?method_list_t?*classMethods;??//category中所有添加的類(lèi)方法的列表

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

struct?property_list_t?*instanceProperties;??//category中添加的所有屬性

}?category_t;

我們看到它是有instanceProperties這樣一個(gè)數(shù)組的,那么為什么說(shuō)無(wú)法添加屬性,其實(shí)是添加后不會(huì)自動(dòng)生成get,set方法而已,所以在使用 點(diǎn)語(yǔ)法的時(shí)候,回報(bào)找不到方法的錯(cuò)誤提示。

因此我們需要使用runtime動(dòng)態(tài)的給category添加對(duì)應(yīng)的get,set方法。

添加屬性的案例,比如給一個(gè)分類(lèi)添加 blog這樣一個(gè)屬性:

@property (nonatomic, copy) NSString *blog;

- (NSString *)blog

{

????// 根據(jù)關(guān)聯(lián)的key,獲取關(guān)聯(lián)的值。

????return objc_getAssociatedObject(self, key);

}

/**

blog的setter方法

*/

- (void)setBlog:(NSString *)blog

{

????// 第一個(gè)參數(shù):給哪個(gè)對(duì)象添加關(guān)聯(lián)

????// 第二個(gè)參數(shù):關(guān)聯(lián)的key,通過(guò)這個(gè)key獲取

????// 第三個(gè)參數(shù):關(guān)聯(lián)的value

????// 第四個(gè)參數(shù):關(guān)聯(lián)的策略

????objc_setAssociatedObject(self, key, blog, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

另外需要注意的有兩點(diǎn):

1)、category的方法沒(méi)有“完全替換掉”原來(lái)類(lèi)已經(jīng)有的方法,也就是說(shuō)如果category和原來(lái)類(lèi)都有methodA,那么category附加完成之后,類(lèi)的方法列表里會(huì)有兩個(gè)methodA。

2)、category的方法被放到了新方法列表的前面,而原來(lái)類(lèi)的方法被放到了新方法列表的后面,這也就是我們平常所說(shuō)的category的方法會(huì)“覆蓋”掉原來(lái)類(lèi)的同名方法,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法,就會(huì)罷休,殊不知后面可能還有一樣名字的方法。

(二)Extension

extension被開(kāi)發(fā)者稱(chēng)之為擴(kuò)展、延展、匿名分類(lèi)。extension看起來(lái)很像一個(gè)匿名的category,但是extension和category幾乎完全是兩個(gè)東西。和category不同的是extension不但可以聲明方法,還可以聲明屬性、成員變量。extension一般用于聲明私有方法,私有屬性,私有成員變量。

比如很的.m 文件里面會(huì)在implementation 上面加個(gè)

@interface viewModel() //這就是extension,可以添加屬性, 方法一般用于私有方法,私有屬性,成員變量

@implementation ViewModel

(三)category和extension的區(qū)別

就category和extension的區(qū)別來(lái)看,我們可以推導(dǎo)出一個(gè)明顯的事實(shí),extension可以添加實(shí)例變量,而category是無(wú)法添加實(shí)例變量的(因?yàn)樵谶\(yùn)行期,對(duì)象的內(nèi)存布局已經(jīng)確定,如果添加實(shí)例變量就會(huì)破壞類(lèi)的內(nèi)部布局,這對(duì)編譯型語(yǔ)言來(lái)說(shuō)是災(zāi)難性的)。

extension在編譯期決議,它就是類(lèi)的一部分,但是category則完全不一樣,它是在運(yùn)行期決議的。extension在編譯期和頭文件里的@interface以及實(shí)現(xiàn)文件里的@implement一起形成一個(gè)完整的類(lèi),它、extension伴隨類(lèi)的產(chǎn)生而產(chǎn)生,亦隨之一起消亡。

extension一般用來(lái)隱藏類(lèi)的私有信息,你必須有一個(gè)類(lèi)的源碼才能為一個(gè)類(lèi)添加extension,所以你無(wú)法為系統(tǒng)的類(lèi)比如NSString添加extension,除非創(chuàng)建子類(lèi)再添加extension。而category不需要有類(lèi)的源碼,我們可以給系統(tǒng)提供的類(lèi)添加category。

extension可以添加實(shí)例變量,而category不可以。

extension和category都可以添加屬性,但是category的屬性不能生成成員變量和getter、setter方法的實(shí)現(xiàn)。

補(bǔ)充:

那么category 中關(guān)聯(lián)的屬性是存放在哪里呢,又如何銷(xiāo)毀:
從runtime的源碼中可以看到,這里其實(shí)所有的關(guān)聯(lián)對(duì)象都由AssociationsManager管理,AssociationsManager里面是由一個(gè)靜態(tài)AssociationsHashMap來(lái)存儲(chǔ)所有的關(guān)聯(lián)對(duì)象的。這相當(dāng)于把所有對(duì)象的關(guān)聯(lián)對(duì)象都存在一個(gè)全局map里面。而map的的key是這個(gè)對(duì)象的指針地址(任意兩個(gè)不同對(duì)象的指針地址一定是不同的),而這個(gè)map的value又是另外一個(gè)AssociationsHashMap,里面保存了關(guān)聯(lián)對(duì)象的kv對(duì)。

銷(xiāo)毀:runtime的銷(xiāo)毀對(duì)象函數(shù)objc_destructInstance里面會(huì)判斷這個(gè)對(duì)象有沒(méi)有關(guān)聯(lián)對(duì)象,如果有,會(huì)調(diào)用_object_remove_assocations做關(guān)聯(lián)對(duì)象的清理工作。

AssociationsManager定義如下:

class AssociationsManager {

? ? static OSSpinLock _lock;

? ? static AssociationsHashMap *_map;? ? ? ? ? ? ? // associative references:? object pointer -> PtrPtrHashMap.public:

? ? AssociationsManager()? { OSSpinLockLock(&_lock); }

? ? ~AssociationsManager()? { OSSpinLockUnlock(&_lock); }

? ? AssociationsHashMap &associations() {

? ? ? ? if (_map == NULL)

? ? ? ? ? ? _map = new AssociationsHashMap();

? ? ? ? return *_map;}}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、accept=“application/zip,application/x-zip,application/x...
    無(wú)名程序猿閱讀 5,832評(píng)論 0 1
  • 每個(gè)月能完成兩三本新書(shū)的閱讀。書(shū)籍類(lèi)別與歷史、哲學(xué)、宗教、靈性成長(zhǎng)為主。一是自然喜歡二是旨在洗心滌慮,提升智慧。閱...
    一箱子的云朵閱讀 798評(píng)論 1 3
  • 隨著市場(chǎng)的不斷開(kāi)放,未來(lái)的市場(chǎng),將出現(xiàn)大量充滿財(cái)富的機(jī)會(huì),又將出現(xiàn)一大批億萬(wàn)富翁,同時(shí)也出現(xiàn)很多人將面臨倒下,絕大...
    公子晗晗閱讀 296評(píng)論 0 0

友情鏈接更多精彩內(nèi)容