關(guān)于Category的原理查近,參考這篇文章:http://tech.meituan.com/DiveIntoCategory.html
Category的使用場(chǎng)景
- 可以把類的實(shí)現(xiàn)分開在幾個(gè)不同的文件里面饵隙。這樣做有幾個(gè)顯而易見的好處:
a)可以減少單個(gè)文件的體積
b)可以把不同的功能組織到不同的category里
c)可以由多個(gè)開發(fā)者共同完成一個(gè)類
d)可以按需加載想要的category - 聲明私有方法
- 模擬多繼承
- 把framework的私有方法公開
Category到底是不能添加成員變量,還是屬性冒冬?還是都不能添加?
屬性和成員變量的區(qū)別,就不介紹了屋摇。
首先,Category在代碼里添加成員變量是根本編譯不過去的幽邓,而添加屬性是可以編譯通過的炮温,如圖:
但是,在Category添加的屬性后牵舵,只會(huì)聲明setter和getter方法茅特,.m文件并未實(shí)現(xiàn)setter和getter方法,會(huì)有黃色預(yù)警棋枕,如圖:
而且白修,如果調(diào)用的話,也不會(huì)被賦值重斑,如圖:
最常見的解決方案就是用runtime手動(dòng)實(shí)現(xiàn)setter和getter方法兵睛,這里就不介紹了。
所以窥浪,剛才那個(gè)問題祖很,Category不能添加成員變量和屬性?就可以有答案了漾脂,不能添加成員變量假颇,可以添加屬性,但是屬性要手動(dòng)實(shí)現(xiàn)setter和getter方法骨稿。
Category為何不能添加成員變量笨鸡,而只能添加方法姜钳?
這要從Category的原理說起,簡(jiǎn)單地說就是通過runtime動(dòng)態(tài)地把Category中的方法等添加到類中(蘋果在實(shí)現(xiàn)的過程中并未將屬性添加到類中形耗,所以屬性僅僅是聲明了setter和getter方法哥桥,而并未實(shí)現(xiàn)),具體請(qǐng)參考http://tech.meituan.com/DiveIntoCategory.html
在Objective-C提供的runtime函數(shù)中激涤,確實(shí)有一個(gè)lass_addIvar()
函數(shù)用于給類添加成員變量拟糕,但是文檔中特別說明:
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
意思是說,這個(gè)函數(shù)只能在“構(gòu)建一個(gè)類的過程中”調(diào)用倦踢。一旦完成類定義送滞,就不能再添加成員變量了。經(jīng)過編譯的類在程序啟動(dòng)后就被runtime加載辱挥,沒有機(jī)會(huì)調(diào)用addIvar犁嗅。程序在運(yùn)行時(shí)動(dòng)態(tài)構(gòu)建的類需要在調(diào)用objc_registerClassPair
之后才可以被使用,同樣沒有機(jī)會(huì)再添加成員變量般贼。
我們?cè)O(shè)想一下如果Objective-C允許動(dòng)態(tài)增加成員變量愧哟,會(huì)發(fā)生什么事情。假設(shè)如下代碼可以執(zhí)行哼蛆。
MyObject *obj = [[MyObject alloc] init];
// 基類增加一個(gè)4字節(jié)的成員變量someVar
class_addIvar([NSObject class], "someVar", 4, ...);
// 基類增加方法someMethod蕊梧,用到了someVar
class_addMethod([NSObject class], @selector(someMethod), ...);
// 調(diào)用someMethod,修改了someVar
[obj someMethod];
// 訪問子類成員變量腮介,會(huì)發(fā)生什么肥矢?
[obj->students length];
顯然,這樣做會(huì)帶來嚴(yán)重問題叠洗,為基類動(dòng)態(tài)增加成員變量會(huì)導(dǎo)致所有已創(chuàng)建出的子類實(shí)例都無法使用甘改,比如上線后的app,如果用戶手機(jī)系統(tǒng)升級(jí)iOS新版本后灭抑,必須重新編譯提交才能在新版系統(tǒng)上運(yùn)行十艾。那為什么runtime允許動(dòng)態(tài)添加方法和屬性,而不會(huì)引發(fā)問題呢腾节?
因?yàn)榉椒ê蛯傩圆⒉弧皩儆凇鳖悓?shí)例忘嫉,而成員變量“屬于”類實(shí)例。我們所說的“類實(shí)例”概念案腺,指的是一塊內(nèi)存區(qū)域庆冕,包含了isa指針和所有的成員變量。所以假如允許動(dòng)態(tài)修改類成員變量布局劈榨,已經(jīng)創(chuàng)建出的類實(shí)例就不符合類定義了访递,變成了無效對(duì)象。但方法定義是在objc_class中管理的同辣,不管如何增刪類方法拷姿,都不影響類實(shí)例的內(nèi)存布局惭载,已經(jīng)創(chuàng)建出的類實(shí)例仍然可正常使用。
需要注意的有兩點(diǎn):
- 1)跌前、category的方法沒有“完全替換掉”原來類已經(jīng)有的方法棕兼,也就是說如果category和原來類都有methodA陡舅,那么category附加完成之后抵乓,類的方法列表里會(huì)有兩個(gè)methodA
- 2)、category的方法被放到了新方法列表的前面靶衍,而原來類的方法被放到了新方法列表的后面灾炭,這也就是我們平常所說的category的方法會(huì)“覆蓋”掉原來類的同名方法,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的颅眶,它只要一找到對(duì)應(yīng)名字的方法蜈出,就會(huì)罷休_,殊不知后面可能還有一樣名字的方法涛酗。
參考:http://quotation.github.io/objc/2015/05/21/objc-runtime-ivar-access.html