我們知道撒顿,所有的OC類和對(duì)象,在runtime層都是用struct表示的,category也不例外突想,在runtime層,category用結(jié)構(gòu)體category_t(在objc-runtime-new.h中可以找到此定義),它包含了:
1)猾担、類的名字(name)
2)袭灯、類(cls)
3)、category中所有給類添加的實(shí)例方法的列表(instanceMethods)
4)绑嘹、category中所有添加的類方法的列表(classMethods)
5)稽荧、category實(shí)現(xiàn)的所有協(xié)議的列表(protocols)
6)、category中添加的所有屬性(instanceProperties)
typedef 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;
} category_t;
從category的定義也可以看出category的可為(可以添加實(shí)例方法工腋,類方法姨丈,甚至可以實(shí)現(xiàn)協(xié)議,添加屬性)和不可為(無法添加實(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ì)罷休^_^,殊不知后面可能還有一樣名字的方法价淌。
分類(category) > 本類 > 父類申眼。即,優(yōu)先調(diào)用cateory中的方法蝉衣,然后調(diào)用本類方法括尸,最后調(diào)用父類方法。
注意:category是在運(yùn)行時(shí)加載的病毡,不是在編譯時(shí)濒翻。
category不能添加成員變量
實(shí)際上,Category實(shí)際上允許添加屬性的啦膜,同樣可以使用@property有送,但是不會(huì)生成_變量(帶下劃線的成員變量),也不會(huì)生成添加屬性的getter和setter方法的實(shí)現(xiàn)僧家,所以雀摘,盡管添加了屬性,也無法使用點(diǎn)語法調(diào)用getter和setter方法(實(shí)際上八拱,點(diǎn)語法是可以寫的阵赠,只不過在運(yùn)行時(shí)調(diào)用到這個(gè)方法時(shí)候會(huì)報(bào)方法找不到的錯(cuò)誤涯塔,如下圖)。但實(shí)際上可以使用runtime去實(shí)現(xiàn)Category為已有的類添加新的屬性并生成getter和setter方法
需要注意的有兩點(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ì)罷休真屯,殊不知后面可能還有一樣名字的方法脸候。
分類的執(zhí)行優(yōu)先級(jí)
4.1在本類和分類有相同的方法時(shí),優(yōu)先調(diào)用分類的方法再調(diào)用本類的方法绑蔫。
4.2如果有兩個(gè)分類运沦,他們都實(shí)現(xiàn)了相同的方法,如何判斷誰先執(zhí)行配深?分類執(zhí)行順序可以通過targets,Build Phases,Complie Source進(jìn)行調(diào)節(jié)携添,注意執(zhí)行順序是從上到下的。(只有兩個(gè)相同方法名的分類)
1)無法向類中添加新的實(shí)例變量篓叶。
(2)名稱沖突烈掠,即當(dāng)類別中的方法與原始類方法名稱沖突時(shí),類別具有更高的優(yōu)先級(jí)缸托。
(3)如果多個(gè)分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時(shí)候執(zhí)行誰由編譯器決定左敌;編譯器會(huì)執(zhí)行最后一個(gè)參與編譯的分類中的方法
實(shí)例變量本質(zhì)上就是成員變量,只是實(shí)例是針對(duì)類而言俐镐,實(shí)例是指類的聲明矫限。{?? }中的yourButton就是實(shí)例變量。id 是OC特有的類佩抹,本質(zhì)上講id等同于(void *)叼风。所以id data屬于實(shí)例變量。
成員變量用于類內(nèi)部棍苹,無需與外界接觸的變量无宿。因?yàn)槌蓡T變量不會(huì)生成set、get方法枢里,所以外界無法與成員變量接觸懈贺。根據(jù)成員變量的私有性经窖,為了方便訪問,所以就有了屬性變量梭灿。屬性變量的好處就是允許讓其他對(duì)象訪問到該變量(因?yàn)閷傩詣?chuàng)建過程中自動(dòng)產(chǎn)生了set 和get方法)画侣。當(dāng)然,你可以設(shè)置只讀或者可寫等堡妒,設(shè)置方法也可自定義配乱。所以,屬性變量是用于與其他對(duì)象交互的變量皮迟。
可以看到在接口 @interface 括號(hào)里面的統(tǒng)稱為”成員變量”搬泥,實(shí)例變量是成員變量中的一種!
實(shí)例變量的英文翻譯是?Instance?Variable?(object-specific storage)?
實(shí)例的英文翻譯為Instance(manifestation? of a? class)? 說的是“類的表現(xiàn)”伏尼,說明實(shí)例變量應(yīng)該是由類定義的變量忿檩!
除去基本數(shù)據(jù)類型int float ....等,其他類型的變量都叫做實(shí)例變量爆阶。
**實(shí)例變量+基本數(shù)據(jù)類型變量=成員變量**
1.分類是用于給原有類添加方法的,因?yàn)榉诸惖慕Y(jié)構(gòu)體指針中燥透,沒有屬性列表,只有方法列表辨图。所以< 原則上講它只能添加方法, 不能添加屬性(成員變量),實(shí)際上可以通過其它方式添加屬性> ;
2.分類中的可以寫@property, 但不會(huì)生成setter/getter方法, 也不會(huì)生成實(shí)現(xiàn)以及私有的成員變量(編譯時(shí)會(huì)報(bào)警告);
3.可以在分類中訪問原有類中.h中的屬性;
4.如果分類中有和原有類同名的方法, 會(huì)優(yōu)先調(diào)用分類中的方法, 就是說會(huì)忽略原有類的方法班套。所以同名方法調(diào)用的優(yōu)先級(jí)為 分類 > 本類 > 父類。因此在開發(fā)中盡量不要覆蓋原有類;
5.如果多個(gè)分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時(shí)候執(zhí)行誰由編譯器決定故河;編譯器會(huì)執(zhí)行最后一個(gè)參與編譯的分類中的方法吱韭。
分類與類擴(kuò)展的區(qū)別:
1 類別中原則上智能增加方法
能添加屬性的問題只是通過runtime解決沒有setter/getter的問題而已
2 類擴(kuò)展不僅可以增加方法,還可以增加實(shí)例變量(或者屬性)鱼的,只是該實(shí)例變量默認(rèn)只是@private類型的(只能在自身類使用理盆,不能再子類或者其他地方使用)
3 類擴(kuò)展中聲明的方法沒有被實(shí)現(xiàn),編譯器會(huì)報(bào)警凑阶,但是類別中聲明的方法沒有被實(shí)現(xiàn)熏挎,編譯器也會(huì)有警告。這是因?yàn)轭悢U(kuò)展是在編譯階段被添加到類中晌砾,而類別是在運(yùn)行時(shí)添加到類中
4 類擴(kuò)展不能像類別那樣擁有獨(dú)立的實(shí)現(xiàn)部分 @implementaton 部分坎拐。也就是說,類擴(kuò)展所聲明的方法必須依托對(duì)應(yīng)的類的實(shí)現(xiàn)部分來實(shí)現(xiàn)
5 定義在.m文件中的類擴(kuò)展方法為私有方法养匈,定義在.h中的類擴(kuò)展的方法為公有方法哼勇。