范疇
范疇:
實(shí)現(xiàn)某個(gè)類的一部分方法的模塊叫作范疇或類別。
范疇和類一樣,都是在接口文件中聲明捷雕,在類文件中實(shí)現(xiàn)。但范疇中不能聲明實(shí)例變量壹甥,只能聲明方法救巷,聲明的方法既可以是類方法,也可以是實(shí)例方法句柠。
范疇的語法如下所示浦译。“類名”部分為范疇所屬的類的名字或即將添加該范疇的類的名字溯职【眩“類名”必須已經(jīng)存在的類,不能為一個(gè)不存在的類定義范疇:
//范疇的聲明
@interface類名(范疇名)
方法的聲明谜酒;
...
@end
//范疇的實(shí)現(xiàn)
@implementation類名(范疇名)
方法的定義叹俏;
...
@end
范疇和文件的組織:
包含主文件接口的頭文件中也包含了其他所有接口。
另外一種定義范疇的方法就是將每個(gè)范疇部分都單獨(dú)定義成一個(gè)頭文件僻族,每個(gè)實(shí)現(xiàn)文件都可以被單獨(dú)編譯粘驰。實(shí)現(xiàn)文件一般被命名為:“類名+范疇名.m”
范疇的接口部分需要遵循以下幾個(gè)原則:
范疇的接口部分必須引用主文件的接口文件
范疇的實(shí)現(xiàn)部分必須引用對(duì)應(yīng)的接口文件
使用范疇中的方法時(shí)必須引用這個(gè)方法所在的頭文件
作為子模塊的范疇:
上面講述的內(nèi)容的前提都是類的規(guī)模非常大,這種情況下通過使用范疇可以提高開發(fā)效率述么,但實(shí)際上規(guī)模大的類并不是一個(gè)好的選擇蝌数。在設(shè)計(jì)階段,如果發(fā)現(xiàn)一個(gè)類的方法非常多度秘,最好把這個(gè)類的功能分成幾個(gè)類來實(shí)現(xiàn)顶伞。
方法的前向聲明:
未在接口部分中聲明的局部方法,和未聲明的C語言函數(shù)一樣剑梳,其定義之前的代碼是沒法使用他們的唆貌。而通過使用范疇,就可以解決這個(gè)問題阻荒。類似于C語言中通過在文件的最前面加上函數(shù)聲明來解決類似的問題挠锥。
把局部方法都加入到一個(gè)局部的范疇中,并在實(shí)現(xiàn)文件的頭部加上范疇的聲明和定義侨赡。
將類劃分多個(gè)范疇之后蓖租,就必須考慮如何劃分共有方法。
如圖羊壹,劃分方法是創(chuàng)建一個(gè)對(duì)外不公開的接口文件蓖宦,其中聲明的類的共有變量和方法。像這樣油猫,即使在多個(gè)文件中進(jìn)行了共同的聲明稠茂,也可以定義僅供類內(nèi)文件使用而不對(duì)類外公開的方法和實(shí)例變量。這種方法叫私有方法。
為避免子類將父類的私有方法名覆蓋睬关,推薦為私有方法名加上固定前綴诱担。
類擴(kuò)展:
把類分為多個(gè)范疇來實(shí)現(xiàn)的情況下,主類和各個(gè)范疇都是獨(dú)立的电爹,每個(gè)范疇都不清楚其他的部分蔫仙。有的范疇可能是執(zhí)行前加載的,有的范疇可能是執(zhí)行時(shí)動(dòng)態(tài)加載的丐箩。這種實(shí)現(xiàn)方法的可擴(kuò)展性非常好摇邦,但編譯器在鏈接時(shí)不會(huì)檢查是否所有的范疇都被鏈接到了可執(zhí)行文件中。針對(duì)這種情況屎勘,引入了類擴(kuò)展的概念施籍。
類擴(kuò)展的聲明和范疇比較相似,只是圓括號(hào)之間沒有文本概漱。類擴(kuò)展中也可以增加實(shí)例變量:
@interface
Card () {
BOOLflag;
}
- (BOOL)hasSameSuit:(Card *)obj;
@end
類擴(kuò)展中聲明的方法需要在類的實(shí)現(xiàn)文件中實(shí)現(xiàn)丑慎,不管是否引入了類擴(kuò)展的定義,只要在類的實(shí)現(xiàn)部分中沒實(shí)現(xiàn)對(duì)應(yīng)的方法犀概,就會(huì)出現(xiàn)編譯錯(cuò)誤立哑。也就是說,通過將一定要實(shí)現(xiàn)的私有方法放入到類擴(kuò)展中姻灶,就可以防止忘記實(shí)現(xiàn)這個(gè)方法或漏掉方法的實(shí)現(xiàn)等問題铛绰。
但是,類擴(kuò)展自身并不會(huì)讓方法變?yōu)樗接蟹椒ú恚绻胱尫椒ㄗ兂伤接蟹椒ㄎ骊仨毑荒茏尠祟悢U(kuò)展的頭文件對(duì)外公開。
類擴(kuò)展中聲明的變量只能在引入了類的主接口和擴(kuò)展聲明的范疇中使用曾沈。主類的實(shí)現(xiàn)部分也可以聲明實(shí)例變量这嚣,但實(shí)例變量只在該文件(實(shí)現(xiàn)文件)中有效。與此相對(duì)塞俱,類擴(kuò)展中定義的實(shí)例變量可以在多個(gè)范疇中使用姐帚。
范疇和屬性聲明:
范疇的接口中可以包含屬性聲明,但范疇的實(shí)現(xiàn)部分不能包含@synthesize障涯。實(shí)現(xiàn)部分需手動(dòng)定義訪問方法罐旗。這是為了防止隨意訪問同一個(gè)類的不同文件中定義的實(shí)例變量。
類擴(kuò)展中也可以包含屬性聲明唯蝶,屬性是通過在類的實(shí)現(xiàn)部分包含@synthesize或者屬性方法來實(shí)現(xiàn)的九秀。類擴(kuò)展中也可以聲明實(shí)例變量的屬性。
類擴(kuò)展只能引用類內(nèi)的實(shí)例或方法粘我,至于范疇的頭文件是公開還是僅限類內(nèi)使用鼓蜒,則是由具體的使用情況決定的。
給現(xiàn)有類追加范疇
追加新的方法:
無論是自己定義的類還是系統(tǒng)的類,利用范疇都可以為已有類追加新方法都弹。
利用范疇只可以追加方法娇豫,不可以追加實(shí)例變量。
我們一般通過繼承的方式為現(xiàn)有類增加新的功能畅厢,但有時(shí)候使用子類并不方便锤躁,例如無法用通常的方法來為NSString創(chuàng)建子類等。而范疇的好處就是可以為正在使用的類增加新功能或详。
雖然范疇追加方法很方便,但也不要濫用郭计。
覆蓋已有方法:
新定義的范疇中的方法如果和原有方法重名的話霸琴,新定義的方法就會(huì)覆蓋老方法。但是不建議利用范疇覆蓋已有的方法昭伸。
關(guān)聯(lián)引用
關(guān)聯(lián)引用的概念:
通過范疇可以為一個(gè)類追加新的方法但不能追加實(shí)例變量梧乘。但是,利用Objective-C的動(dòng)態(tài)性庐杨,并借助運(yùn)行時(shí)(runtime)的功能选调,就可以為已存在的實(shí)例對(duì)象增加實(shí)例變量,這個(gè)功能叫做關(guān)聯(lián)引用(associative references)灵份。將這個(gè)功能和范疇組合在一起使用仁堪,即使不創(chuàng)建子類,也能夠?qū)︻愡M(jìn)行動(dòng)態(tài)擴(kuò)展填渠。
添加關(guān)聯(lián)和檢索關(guān)聯(lián):
這兩個(gè)方法定義在objc/runtime.h中弦聂。
voidobjc_setAssociatedObject(idobject,void*key,idvalue, objc_AssociationPolicy policy)
idobjc_getAssociatedObject(idobject,void*key)
在本章中,把要增加關(guān)聯(lián)的對(duì)象(想擴(kuò)展的對(duì)象)稱為所有者氛什,把追加的對(duì)象稱為引用對(duì)象莺葫。key的值必須使用確定的,不再改變的地址作為鍵值枪眉。使用地址作為key的原因是為了保證唯一性捺檬,key不會(huì)被用來存儲(chǔ)什么,也不會(huì)對(duì)其進(jìn)行賦值操作贸铜。
對(duì)象的存儲(chǔ)方法:
objc_setAssociatedObject的第四個(gè)參數(shù)policy是關(guān)聯(lián)策略堡纬。關(guān)聯(lián)策略表明了關(guān)聯(lián)對(duì)象是通過何種方式進(jìn)行關(guān)聯(lián)的,以及這種關(guān)聯(lián)是原子的還是非原子的萨脑。policy的值有以下幾種可供選擇隐轩,其中最常用的是OBJC_ASSOCIATION_RETAIN.
OBJC_ASSOCIATION_ASSIGN
基于引用計(jì)數(shù)的內(nèi)存管理時(shí),不會(huì)給關(guān)聯(lián)對(duì)象發(fā)送retain渤早,僅僅通過賦值進(jìn)行關(guān)聯(lián)职车。內(nèi)存管理使用垃圾回收時(shí),會(huì)把引用對(duì)象作為弱引用保存。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
使用基于引用計(jì)數(shù)的內(nèi)存管理時(shí)悴灵,會(huì)給關(guān)聯(lián)對(duì)象發(fā)送retain消息并保持扛芽,如果同樣的key已經(jīng)關(guān)聯(lián)到其他對(duì)象,則會(huì)給其他對(duì)象發(fā)送release积瞒。釋放關(guān)聯(lián)對(duì)象的所有者時(shí)川尖,會(huì)給所有的關(guān)聯(lián)對(duì)象發(fā)送release消息。內(nèi)存使用垃圾回收時(shí)茫孔,會(huì)以強(qiáng)引用的形式保存關(guān)聯(lián)對(duì)象叮喳。
OBJC_ASSOCIATION_RETAIN
功能和上一個(gè)一樣,唯一的區(qū)別是OBJC_ASSOCIATION_RETAIN是多線程安全的缰贝,支持排他性關(guān)聯(lián)操作馍悟。
OBJC_ASSOCIATION_COPY_NONATOMIC
在進(jìn)行對(duì)象關(guān)聯(lián)的時(shí)候會(huì)復(fù)制一份原對(duì)象,并用新復(fù)制的對(duì)象進(jìn)行關(guān)聯(lián)操作剩晴。
OBJC_ASSOCIATION_COPY
功能和上一個(gè)一樣锣咒,唯一的區(qū)別是OBJC_ASSOCIATION_COPY時(shí)多線程安全的,支持排他性的關(guān)聯(lián)操作赞弥。
斷開關(guān)聯(lián):
voidobjc_removeAssociatedObjects(idobject)
通過范疇擴(kuò)展添加的實(shí)例變量并不是真正的實(shí)例變量毅整,所以對(duì)象復(fù)制和歸檔要特別注意。