Objectiive-C Category

本文主要介紹Objectiive-C的Category,當(dāng)你需要擴(kuò)展系統(tǒng)SDK提供的類的時(shí)候,Category就十分有用.Category允許你為已經(jīng)存在的類添加屬性或者方法,供自己使用.

Category添加的屬性和方法是可以供類和其子類使用的.

1.Category 添加實(shí)例方法,類方法和屬性:

@interface UIView (TagID)

-(void)logUIView;

+(void)debugLogUIView;

@property (nonatomic,copy)NSString * tagName;

@end

-(void)setTagName:(NSString *)tagName{

? ? objc_setAssociatedObject(self, "tagName", tagName, OBJC_ASSOCIATION_COPY);

}

-(NSString *)tagName{

? return objc_getAssociatedObject(self, "tagName");

}

-(void)logUIView{

? ? NSLog(@"self-->%@ infomation-->%@",[self class],[self description]);

}

+(void)debugLogUIView{


? ? NSLog(@"self-->%@ info-->%@",self,[self debugDescription]);

}

2.Category 是不能添加實(shí)例變量的,因?yàn)閏ategory是在運(yùn)行時(shí)才會(huì)去添加,此時(shí)對(duì)象的內(nèi)存布局已經(jīng)確定,不能添加實(shí)例變量.

3.Objectiive-C的Category 實(shí)現(xiàn)原理

Runtime源碼中Category的結(jié)構(gòu):

struct category_t {

? ? const char *name;

? ? classref_t cls;

? ? struct method_list_t *instanceMethods;

? ? struct method_list_t *classMethods;

? ? struct protocol_list_t *protocols;

? ? struct property_list_t *instanceProperties;

? ? // Fields below this point are not always present on disk.

? ? struct property_list_t *_classProperties;

? ? method_list_t *methodsForMeta(bool isMeta) {

? ? ? ? if (isMeta) return classMethods;

? ? ? ? else return instanceMethods;

? ? }

? ? property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);

};

可以看到Category 的結(jié)構(gòu)體中存在名字,類,實(shí)例方法,類方法,協(xié)議,實(shí)例屬性,類屬性,還有Category結(jié)構(gòu)體自帶的方法methodsForMeta(該方法用于返回Categroy的方法).

同時(shí)Runtime會(huì)把Categroy添加到類上

static void

attachCategories(Class cls, category_list *cats, bool flush_caches)

{

? ? if (!cats) return;

? ? if (PrintReplacedMethods) printReplacements(cls, cats);

//? 獲取當(dāng)前類是不是MetaClass

? ? bool isMeta = cls->isMetaClass();

//為方法列表,和屬性列表,協(xié)議列表開(kāi)辟內(nèi)存空間

? ? // fixme rearrange to remove these intermediate allocations

? ? method_list_t **mlists = (method_list_t **)

? ? ? ? malloc(cats->count * sizeof(*mlists));

? ? property_list_t **proplists = (property_list_t **)

? ? ? ? malloc(cats->count * sizeof(*proplists));

? ? protocol_list_t **protolists = (protocol_list_t **)

? ? ? ? malloc(cats->count * sizeof(*protolists));

//循環(huán)把Categroy加到類上

? ? // Count backwards through cats to get newest categories first

? ? int mcount = 0;

? ? int propcount = 0;

? ? int protocount = 0;

? ? int i = cats->count;

? ? bool fromBundle = NO;

? ? while (i--) {

? ? ? ? auto& entry = cats->list[i];

//? ? ? 獲取Categroy方法列表

? ? ? ? method_list_t *mlist = entry.cat->methodsForMeta(isMeta);

? ? ? ? if (mlist) {

? ? ? ? ? ? mlists[mcount++] = mlist;

? ? ? ? ? ? fromBundle |= entry.hi->isBundle();

? ? ? ? }

//獲取Categroy屬性列表

? ? ? ? property_list_t *proplist =

? ? ? ? ? ? entry.cat->propertiesForMeta(isMeta, entry.hi);

? ? ? ? if (proplist) {

? ? ? ? ? ? proplists[propcount++] = proplist;

? ? ? ? }

//獲取Categroy協(xié)議列表

? ? ? ? protocol_list_t *protolist = entry.cat->protocols;

? ? ? ? if (protolist) {

? ? ? ? ? ? protolists[protocount++] = protolist;

? ? ? ? }

? ? }

//獲取類的符號(hào)結(jié)構(gòu)

? ? auto rw = cls->data();

? ? prepareMethodLists(cls, mlists, mcount, NO, fromBundle);

//? ? 將Catrgroy的方法列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)

? ? rw->methods.attachLists(mlists, mcount);

? ? free(mlists);

? ? if (flush_caches? &&? mcount > 0) flushCaches(cls);

//將Catgroy的屬性列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)

? ? rw->properties.attachLists(proplists, propcount);

? ? free(proplists);

//將Catgroy的協(xié)議列表添加到類上(copy到類的方法列表數(shù)組內(nèi)部)

? ? rw->protocols.attachLists(protolists, protocount);

? ? free(protolists);

}

下面這段代碼就是attachLists的實(shí)現(xiàn)

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;

? ? ? ? ? ? setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));

? ? ? ? ? ? array()->count = newCount;

//? ? ? ? ? ? 將原來(lái)的數(shù)組移動(dòng)到新數(shù)組的addedCount位置之后

? ? ? ? ? ? memmove(array()->lists + addedCount, array()->lists,

? ? ? ? ? ? ? ? ? ? oldCount * sizeof(array()->lists[0]));

//? ? ? ? ? ? 將addedLists copy到新數(shù)組的首位開(kāi)始的位置

? ? ? ? ? ? memcpy(array()->lists, addedLists,

? ? ? ? ? ? ? ? ? addedCount * sizeof(array()->lists[0]));

? ? ? ? }

? ? ? ? else if (!list? &&? addedCount == 1) {

? ? ? ? ? ? // 0 lists -> 1 list

? ? ? ? ? ? list = addedLists[0];

? ? ? ? }

? ? ? ? else {

? ? ? ? ? ? // 1 list -> many lists

? ? ? ? ? ? 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;

//? ? ? ? ? ? 將addedLists copy到新數(shù)組的首位開(kāi)始的位置

? ? ? ? ? ? memcpy(array()->lists, addedLists,

? ? ? ? ? ? ? ? ? addedCount * sizeof(array()->lists[0]));

? ? ? ? }

? ? }

category的方法被放到了新方法列表的前面,而原來(lái)類的方法被放到了新方法列表的后面妻熊,這也就是我們平常所說(shuō)的category的方法會(huì)“覆蓋”掉原來(lái)類的同名方法夸浅,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的,它只要一找到對(duì)應(yīng)名字的方法扔役,就會(huì)停止查找.

關(guān)于Categroy的關(guān)聯(lián)對(duì)象

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {

? ? // retain the new value (if any) outside the lock.

? ? ObjcAssociation old_association(0, nil);

//? ? 如果value存在根據(jù)policy發(fā)送SEL_retain 或SEL_copy

? ? id new_value = value ? acquireValue(value, policy) : nil;

? ? {

//? ? ? ? 獲取到AssociationsManager的hashMap這是儲(chǔ)存所有關(guān)聯(lián)對(duì)象的hashMap

? ? ? ? AssociationsManager manager;

? ? ? ? AssociationsHashMap &associations(manager.associations());

? ? ? ? disguised_ptr_t disguised_object = DISGUISE(object);

? ? ? ? if (new_value) {

? ? ? ? ? ? // break any existing association.

//? ? ? ? ? ? 迭代hashMap并存儲(chǔ)關(guān)聯(lián)的對(duì)象

? ? ? ? ? ? AssociationsHashMap::iterator i = associations.find(disguised_object);

? ? ? ? ? ? if (i != associations.end()) {

? ? ? ? ? ? ? ? // secondary table exists

? ? ? ? ? ? ? ? ObjectAssociationMap *refs = i->second;

? ? ? ? ? ? ? ? ObjectAssociationMap::iterator j = refs->find(key);

? ? ? ? ? ? ? ? if (j != refs->end()) {

? ? ? ? ? ? ? ? ? ? old_association = j->second;

? ? ? ? ? ? ? ? ? ? j->second = ObjcAssociation(policy, new_value);

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? (*refs)[key] = ObjcAssociation(policy, new_value);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? // create the new association (first time).

? ? ? ? ? ? ? ? ObjectAssociationMap *refs = new ObjectAssociationMap;

? ? ? ? ? ? ? ? associations[disguised_object] = refs;

? ? ? ? ? ? ? ? (*refs)[key] = ObjcAssociation(policy, new_value);

? ? ? ? ? ? ? ? object->setHasAssociatedObjects();

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? // setting the association to nil breaks the association.

? ? ? ? ? ? AssociationsHashMap::iterator i = associations.find(disguised_object);

? ? ? ? ? ? if (i !=? associations.end()) {

? ? ? ? ? ? ? ? ObjectAssociationMap *refs = i->second;

? ? ? ? ? ? ? ? ObjectAssociationMap::iterator j = refs->find(key);

? ? ? ? ? ? ? ? if (j != refs->end()) {

? ? ? ? ? ? ? ? ? ? old_association = j->second;

? ? ? ? ? ? ? ? ? ? refs->erase(j);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? // release the old value (outside of the lock).

? ? if (old_association.hasValue()) ReleaseValue()(old_association);

}

Runtime采用的是全局的HashMap存儲(chǔ)關(guān)聯(lián)對(duì)象.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞄桨,一起剝皮案震驚了整個(gè)濱河市孩饼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖盈滴,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異古掏,居然都是意外死亡帜平,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門序仙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)突颊,“玉大人,你說(shuō)我怎么就攤上這事潘悼÷赏海” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵治唤,是天一觀的道長(zhǎng)棒动。 經(jīng)常有香客問(wèn)我,道長(zhǎng)宾添,這世上最難降的妖魔是什么船惨? 我笑而不...
    開(kāi)封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮缕陕,結(jié)果婚禮上粱锐,老公的妹妹穿的比我還像新娘。我一直安慰自己扛邑,他們只是感情好卜范,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著鹿榜,像睡著了一般海雪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舱殿,一...
    開(kāi)封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天奥裸,我揣著相機(jī)與錄音,去河邊找鬼沪袭。 笑死湾宙,一個(gè)胖子當(dāng)著我的面吹牛樟氢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侠鳄,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼埠啃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伟恶?” 一聲冷哼從身側(cè)響起碴开,我...
    開(kāi)封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎博秫,沒(méi)想到半個(gè)月后潦牛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挡育,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年巴碗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片即寒。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橡淆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出母赵,到底是詐尸還是另有隱情明垢,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布市咽,位于F島的核電站,受9級(jí)特大地震影響抵蚊,放射性物質(zhì)發(fā)生泄漏施绎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一贞绳、第九天 我趴在偏房一處隱蔽的房頂上張望谷醉。 院中可真熱鬧,春花似錦冈闭、人聲如沸俱尼。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遇八。三九已至,卻和暖如春耍休,著一層夾襖步出監(jiān)牢的瞬間刃永,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工羊精, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斯够,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像读规,于是被迫代替她去往敵國(guó)和親抓督。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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