1.Category是什么捍岳?重寫一個(gè)類的方法用繼承好還是分類好?
? ?Category是類別沉衣;一般情況用分類好郁副,用Category去重寫類的方法,僅對(duì)本Category有效豌习,不會(huì)影響到其他類與原有類的關(guān)系存谎。
category好處:
可以減少單個(gè)文件的體積
可以把不同的功能組織到不同的category里
可以由多個(gè)開發(fā)者共同完成一個(gè)類
可以按需加載想要的category
聲明私有方法
category特點(diǎn)
category只能給某個(gè)已有的類擴(kuò)充方法,不能擴(kuò)充成員變量肥隆。
category中也可以添加屬性既荚,只不過@property只會(huì)生成setter和getter的聲明,不會(huì)生成setter和getter的實(shí)現(xiàn)以及成員變量栋艳。
如果category中的方法和類中原有方法同名恰聘,運(yùn)行時(shí)會(huì)優(yōu)先調(diào)用category中的方法。也就是吸占,category中的方法會(huì)覆蓋掉類中原有的方法晴叨。所以開發(fā)中盡量保證不要讓分類中的方法和原有類中的方法名相同。避免出現(xiàn)這種情況的解決方案是給分類的方法名統(tǒng)一添加前綴矾屯。比如category_兼蕊。
如果多個(gè)category中存在同名的方法,運(yùn)行時(shí)到底調(diào)用哪個(gè)方法由編譯器決定件蚕,最后一個(gè)參與編譯的方法會(huì)被調(diào)用孙技。
2.Category的實(shí)現(xiàn)原理或者本質(zhì) 产禾?
? ?Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類的對(duì)象方法绪杏、類方法下愈、屬性、協(xié)議信息
? ? 在程序運(yùn)行的時(shí)候蕾久,runtime會(huì)將Category的數(shù)據(jù)合并到類信息中(類對(duì)象势似、元類對(duì)象中)
? ? 分類的實(shí)現(xiàn)原理是將category中的方法,屬性僧著,協(xié)議數(shù)據(jù)放在category_t結(jié)構(gòu)體中履因,然后將結(jié)構(gòu)體內(nèi)的方法列表拷貝到類對(duì)象的方法列表中。?
3.Category的加載處理過程是什么盹愚?
? ?通過Runtime加載某個(gè)類的所有Category數(shù)據(jù)
? ? 把所有Category的方法栅迄、屬性、協(xié)議數(shù)據(jù)皆怕,合并到一個(gè)大數(shù)組中
? ? 后面參與編譯的Category數(shù)據(jù)毅舆,會(huì)在數(shù)組的前面
? ? 將合并后的分類數(shù)據(jù)(方法、屬性愈腾、協(xié)議)憋活,插入到類原來數(shù)據(jù)的前面
可以理解為:
? ? 把 category 的實(shí)例方法、協(xié)議以及屬性添加到類上虱黄。
? ? 把 category 的類方法和協(xié)議添加到類的 metaclass 上悦即。
其中需要注意的是:
category 的方法沒有「完全替換掉」原來類已經(jīng)有的方法,也就是說如果 category 和原來類都有 methodA橱乱,那么 category 附加完成之后辜梳,類的方法列表里會(huì)有兩個(gè) methodA。
category 的方法被放到了新方法列表的前面泳叠,而原來類的方法被放到了新方法列表的后面作瞄,這也就是我們平常所說的category 的方法會(huì)「覆蓋」掉原來類的同名方法,這是因?yàn)檫\(yùn)行時(shí)在查找方法的時(shí)候是順著方法列表的順序查找的析二,它只要一找到對(duì)應(yīng)名字的方法粉洼,就會(huì)返回,不會(huì)管后面可能還有一樣名字的方法叶摄。
4.Category為什么不能添加成員變量?
Category 可以給類增加方法和屬性,但是并不會(huì)自動(dòng)生成成員變量及set/get方法安拟。因?yàn)閏ategory_t結(jié)構(gòu)體中并不存在成員變量蛤吓。我們知道成員變量是存放在實(shí)例對(duì)象中的,并且編譯的那一刻就已經(jīng)決定好了糠赦。而分類是在運(yùn)行時(shí)才去加載的会傲。那么我們就無法再程序運(yùn)行時(shí)將分類的成員變量中添加到實(shí)例對(duì)象的結(jié)構(gòu)體中锅棕。因此分類中不可以添加成員變量。
在 Objective-C 提供的 runtime 函數(shù)中淌山,確實(shí)有一個(gè) class_addIvar() 函數(shù)用于給類添加成員變量裸燎,但是閱讀過蘋果的官方文檔的人應(yīng)該會(huì)看到:
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)用泼疑。當(dāng)編譯類的時(shí)候德绿,編譯器生成了一個(gè)實(shí)例變量?jī)?nèi)存布局 ivar layout,來告訴運(yùn)行時(shí)去那里訪問類的實(shí)例變量們退渗,一旦完成類定義移稳,就不能再添加成員變量了。經(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ì)再添加成員變量翻翩。
不能為一個(gè)類動(dòng)態(tài)的添加成員變量都许,可以給類動(dòng)態(tài)增加方法和屬性。因?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í)例仍然可正常使用。
5.Category中有l(wèi)oad方法嗎瘪校?load方法是什么時(shí)候調(diào)用的澄暮?load 方法能繼承嗎?
在類和 category 中都可以有?+load方法,load方法在程序啟動(dòng)裝載類信息的時(shí)候就會(huì)調(diào)用阱扬。load方法可以繼承泣懊。調(diào)用子類的load方法之前,會(huì)先調(diào)用父類的load方法.
6.在類的?+load方法調(diào)用的時(shí)候麻惶,我們可以調(diào)用 category 中聲明的方法么馍刮?
可以調(diào)用,因?yàn)楦郊?category 到類的工作會(huì)先于?+load方法的執(zhí)行窃蹋。
7.這么些個(gè)+load方法卡啰,調(diào)用順序是咋樣的呢静稻?
+load的執(zhí)行順序是先類,后 category匈辱,而 category 的+load?執(zhí)行順序是根據(jù)編譯順序決定的振湾。雖然對(duì)于?+load的執(zhí)行順序是這樣,但是對(duì)于「覆蓋」掉的方法亡脸,則會(huì)先找到最后一個(gè)編譯的 category 里的對(duì)應(yīng)方法押搪。
子類的+load方法會(huì)在它的所有父類的+load方法之后執(zhí)行,而分類的+load方法會(huì)在它的主類的+load方法之后執(zhí)行梗掰,但是不同的類之間的+load方法的調(diào)用順序是不確定的嵌言,在Compile Sources中,文件的引入的先后順序決定不同的類之間的+load方法的調(diào)用順序及穗。
注意:+load方法調(diào)用時(shí)摧茴,如果調(diào)用別的類的方法,且該方法依賴于那個(gè)類的load方法進(jìn)行初始化設(shè)置埂陆,那么必須確保那個(gè)類的+load方法已經(jīng)調(diào)用了苛白,比如在Other類中調(diào)用Child類里面的方法,則打印出的字符串就為null焚虱。
8.怎么調(diào)用到原來類中被 category 覆蓋掉的方法购裙?
對(duì)于這個(gè)問題,我們已經(jīng)知道 category 其實(shí)并不是完全替換掉原來類的同名方法鹃栽,只是 category 在方法列表的前面而已躏率,所以我們只要順著方法列表找到最后一個(gè)對(duì)應(yīng)名字的方法,就可以調(diào)用原來類的方法
9.load民鼓、initialize的區(qū)別薇芝,以及它們?cè)赾ategory重寫的時(shí)候的調(diào)用的次序。
區(qū)別在于調(diào)用方式和調(diào)用時(shí)刻?
調(diào)用方式:load是根據(jù)函數(shù)地址直接調(diào)用丰嘉,initialize是通過objc_msgSend調(diào)用?
調(diào)用時(shí)刻:load是runtime加載類夯到、分類的時(shí)候調(diào)用(只會(huì)調(diào)用1次),initialize是類第一次接收到消息的時(shí)候調(diào)用饮亏,每一個(gè)類只會(huì)initialize一次(父類的initialize方法可能會(huì)被調(diào)用多次)
調(diào)用順序:
先調(diào)用類的load方法耍贾,先編譯那個(gè)類,就先調(diào)用load路幸。在調(diào)用load之前會(huì)先調(diào)用父類的load方法荐开。分類中l(wèi)oad方法不會(huì)覆蓋本類的load方法,先編譯的分類優(yōu)先調(diào)用load方法简肴。
initialize先初始化父類誓焦,之后再初始化子類。如果子類沒有實(shí)現(xiàn)+initialize着帽,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次)杂伟,如果分類實(shí)現(xiàn)了+initialize,就覆蓋類本身的+initialize調(diào)用仍翰。
參考:http://blog.leichunfeng.com/blog/2015/05/02/objective-c-plus-load-vs-plus-initialize/
10.category是如何為一個(gè)類添加屬性的
利用關(guān)聯(lián)對(duì)象
#import
"MyClass.h"
@interface MyClass (Category1)
@property(nonatomic,copy)NSString*name;
@end
#import "MyClass+Category1.h"
#import <objc/runtime.h>
@implementation MyClass (Category1)
+ (void)load
{
? ? NSLog(@"%@",@"load in Category1");
}
- (void)setName:(NSString *)name
{
? ? objc_setAssociatedObject(self,
? ? ? ? ? ? ? ? ? ? ? ? ? ? "name",
? ? ? ? ? ? ? ? ? ? ? ? ? ? name,
? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC_ASSOCIATION_COPY);
}
- (NSString*)name
{
? ? NSString *nameObject = objc_getAssociatedObject(self, "name");
? ? return nameObject;
}
@end
11.Category(類別)赫粥、 Extension(擴(kuò)展)和繼承的區(qū)別?分別用來做什么?
區(qū)別:
1. 分類有名字予借,類擴(kuò)展沒有分類名字越平,是一種特殊的分類。
2. 分類只能擴(kuò)展方法(屬性僅僅是聲明灵迫,并沒真正實(shí)現(xiàn))秦叛,類擴(kuò)展可以擴(kuò)展屬性、成員變量和方法瀑粥。
3. 繼承可以增加挣跋,修改或者刪除方法,并且可以增加屬性狞换。
就category和extension的區(qū)別來看避咆,我們可以推導(dǎo)出一個(gè)明顯的事實(shí),extension可以添加實(shí)例變量修噪,而category是無法添加實(shí)例變量的(因?yàn)樵谶\(yùn)行期查库,對(duì)象的內(nèi)存布局已經(jīng)確定,如果添加實(shí)例變量就會(huì)破壞類的內(nèi)部布局黄琼,這對(duì)編譯型語言來說是災(zāi)難性的)樊销。
extension在編譯期決議,它就是類的一部分脏款,但是category則完全不一樣围苫,它是在運(yùn)行期決議的。extension在編譯期和頭文件里的@interface以及實(shí)現(xiàn)文件里的@implement一起形成一個(gè)完整的類弛矛,它够吩、extension伴隨類的產(chǎn)生而產(chǎn)生,亦隨之一起消亡丈氓。
extension一般用來隱藏類的私有信息周循,你必須有一個(gè)類的源碼才能為一個(gè)類添加extension,所以你無法為系統(tǒng)的類比如NSString添加extension万俗,除非創(chuàng)建子類再添加extension湾笛。而category不需要有類的源碼,我們可以給系統(tǒng)提供的類添加category闰歪。
extension可以添加實(shí)例變量嚎研,而category不可以。
extension和category都可以添加屬性,但是category的屬性不能生成成員變量和getter临扮、setter方法的實(shí)現(xiàn)论矾。
(二)Extension
1、 什么是extension
extension被開發(fā)者稱之為擴(kuò)展杆勇、延展贪壳、匿名分類。extension看起來很像一個(gè)匿名的category蚜退,但是extension和category幾乎完全是兩個(gè)東西闰靴。和category不同的是extension不但可以聲明方法,還可以聲明屬性钻注、成員變量蚂且。extension一般用于聲明私有方法,私有屬性幅恋,私有成員變量杏死。
2、 extension的存在形式
category是擁有.h文件和.m文件的東西佳遣。但是extension不然识埋。extension只存在于一個(gè).h文件中,或者extension只能寄生于一個(gè)類的.m文件中零渐。比如窒舟,viewController.m文件中通常寄生這么個(gè)東西,其實(shí)這就是一個(gè)extension:
@interface?ViewController?()
@end