前言
?? ?在實(shí)際的開(kāi)發(fā)過(guò)程中,繼承和類(lèi)別都會(huì)得到很多用處机杜。對(duì)于界面相似度很高的情況下帜讲,使用繼承可以節(jié)省很多代碼和設(shè)置,只需要在子類(lèi)中重寫(xiě)父類(lèi)中的方法椒拗,或者增加新的子類(lèi)方法即可似将,代碼非常的簡(jiǎn)潔維護(hù)起來(lái)也很方便。下面小節(jié)下相關(guān)的知識(shí)蚀苛,供需要的朋友查看在验。? ? 在Objective-C中,給一個(gè)類(lèi)擴(kuò)展一個(gè)其它方法堵未,有兩種實(shí)現(xiàn)方式:類(lèi)別和繼承腋舌。
?繼承
?這個(gè)是面向?qū)ο笳Z(yǔ)言都有的一個(gè)特性,子類(lèi)會(huì)繼承父類(lèi)的方法和屬性以及成員變量渗蟹。? 這里說(shuō)的方法需要在 父類(lèi)中的 .h中聲明侦厚,子類(lèi)才可以使用super 調(diào)用父類(lèi)的方法,可以繼承過(guò)來(lái)父類(lèi)的一切屬性拙徽,可以使用父類(lèi)的成員變量刨沦。
? ? .h 文件相當(dāng)于一個(gè)對(duì)外公開(kāi)的 head ,是因?yàn)?oc 中無(wú)法導(dǎo)入 .m 文件膘怕,只能導(dǎo)入.h 文件想诅,所有子類(lèi)中需要用到父類(lèi)中導(dǎo)入的頭文件的話,這個(gè)頭文件需要在 父類(lèi)的.h? 中導(dǎo)入岛心,子類(lèi)無(wú)法調(diào)用 父類(lèi) .m 中導(dǎo)入的頭文件 和聲明的 成員變量来破。所以把 .m 文件中的東西相當(dāng)于是 私有的,不會(huì)被非本類(lèi)的對(duì)象所調(diào)用忘古。
? ? ?在繼承中需要注意的是:重寫(xiě)的這個(gè)方法在父類(lèi)中執(zhí)行時(shí)會(huì)替換掉原來(lái)的方法的(就算子類(lèi)中沒(méi)有調(diào)用這個(gè)新重寫(xiě)的方法徘禁,這個(gè)新方法也已經(jīng)被執(zhí)行了),一般自己不調(diào)用這個(gè)重寫(xiě)的方法髓堪,子類(lèi)一般只調(diào)用新加的方法送朱。
? 在 .h 中聲明成員變量娘荡,又不想被子類(lèi)調(diào)用,可以對(duì)這個(gè)成員變量進(jìn)行限定如:
{
@private
NSMutableArray*modelArray;
}
@private 私有成員驶沼,只有當(dāng)前類(lèi)可以訪問(wèn)炮沐;
@protected 受保護(hù)成員,只有當(dāng)前類(lèi)或子類(lèi)可以訪問(wèn)(如果沒(méi)有添加任何修飾則默認(rèn)為@protected)回怜;
@public 公共成員大年,所有類(lèi)均可訪問(wèn);
類(lèi)別category
這是Objective-C語(yǔ)言的一個(gè)特性玉雾,可以在不改變類(lèi)名和原來(lái)類(lèi)的實(shí)現(xiàn)的前提下翔试,實(shí)現(xiàn)對(duì)類(lèi)的方法擴(kuò)展。
以下兩種方式最好使用類(lèi)別复旬。
1)針對(duì)系統(tǒng)提供的一些類(lèi)遏餐,例如:NSString,NSArray,NSNumber等類(lèi),進(jìn)行方法擴(kuò)充的時(shí)候赢底。
2)類(lèi)別支持開(kāi)發(fā)人員針對(duì)自己構(gòu)建的類(lèi)失都,把相關(guān)的方法分組到多個(gè)單獨(dú)的文件中,對(duì)于大型而復(fù)雜的類(lèi)幸冻,這有助于提高可維護(hù)性粹庞,并簡(jiǎn)化單個(gè)源文件的管理。
對(duì)于以下情況洽损,無(wú)法使用類(lèi)別庞溜,必須使用繼承。
1)新擴(kuò)展的方法與原方法同名碑定,但是還需要使用父類(lèi)的實(shí)現(xiàn)流码。因?yàn)槭褂妙?lèi)別,會(huì)覆蓋原類(lèi)的實(shí)現(xiàn)(繼承也會(huì)覆蓋延刘,就是所謂的重寫(xiě)漫试,但是可以在重寫(xiě)的時(shí)候調(diào)用 ?父類(lèi)的同名方法,而類(lèi)別不能)碘赖,無(wú)法訪問(wèn)到原來(lái)的方法驾荣。
2)擴(kuò)展類(lèi)的屬性,這個(gè)類(lèi)別無(wú)法做到普泡。
OC中的子類(lèi)可以擁有和父類(lèi)相同名稱(chēng)的方法播掷,在子類(lèi)調(diào)用時(shí),優(yōu)先去自己的內(nèi)部尋找撼班,如果沒(méi)有則一層一層的往上找;
(4)OC語(yǔ)言是單繼承語(yǔ)言歧匈。在OC語(yǔ)言中,基本上所有類(lèi)的根類(lèi)都是NSObject類(lèi)砰嘁。
提示:重寫(xiě)即子類(lèi)重新實(shí)現(xiàn)了父類(lèi)中的某個(gè)方法件炉,覆蓋了父類(lèi)以前的實(shí)現(xiàn)勘究。
提示:每個(gè)類(lèi)中都有一個(gè)super class指針,該指針指向自己的父類(lèi)妻率。對(duì)象中有一個(gè)isa指針,該指針指向調(diào)用該對(duì)象的類(lèi)板祝。
繼承的好處:
(1)抽取出了重復(fù)的代碼
(2)建立了類(lèi)和類(lèi)之間的聯(lián)系
繼承的缺點(diǎn):耦合性太強(qiáng)
屬性
在OC中定義變量宫静,可以自己來(lái)定義變量的setter方法來(lái)設(shè)置變量值,用getter方法來(lái)獲取變量值,但是當(dāng)變量數(shù)量增多時(shí)券时,還采用手動(dòng)添加setter/getter方法來(lái)操作變量孤里,就會(huì)使得程序代碼量大大增加,于是就出現(xiàn)了 @property 來(lái)快速聲明設(shè)置獲取變量的值的方法,這也許就是 ?@property @synthesize/@dynamic 設(shè)計(jì)的本意橘洞。
@property是給編輯器看的捌袜。就算你不聲明@property,在obj的@implenmention下寫(xiě)好valueA和setValueA炸枣,還是可以obj.valueA賦值或取值虏等,但是沒(méi)有自動(dòng)聯(lián)想。
只聲明@property而不去實(shí)現(xiàn)适肠,在Xcode4.4以后會(huì)自動(dòng)幫你生成get和set方法
本質(zhì)上來(lái)講霍衫,屬性也會(huì)幫你定義一個(gè)成員變量,并根據(jù)屬性的聲明自動(dòng)生成getter/setter 方法侯养,其中setter 方法根據(jù)屬性(property)的屬性(attribute)來(lái)提供不同的內(nèi)存管理策略敦跌。
@property是一個(gè)屬性訪問(wèn)聲明以及聲明getter,setter方法,
擴(kuò)號(hào)內(nèi)支持以下幾個(gè)屬性:(getter=getterName逛揩,setter=setterName柠傍,設(shè)置setter與getter的方法名,retain,copy辩稽,assign.......)
在聲明property屬性后惧笛,有2種實(shí)現(xiàn)選擇
@synthesize? 作用是實(shí)現(xiàn)屬性的,如getter,setter方法. (通過(guò)類(lèi)別和runtime 的對(duì)相關(guān)聯(lián)技術(shù)生成新的屬性時(shí)逞泄,無(wú)法使用這個(gè)這個(gè)設(shè)置徐紧,只能使用@dynamic)
編譯器期間,讓編譯器自動(dòng)生成getter/setter方法炭懊。
當(dāng)有自定義的存或取方法時(shí)并级,自定義會(huì)屏蔽自動(dòng)生成該方法
@dynamic
告訴編譯器,不自動(dòng)生成getter/setter方法侮腹,避免編譯期間產(chǎn)生警告
然后由自己實(shí)現(xiàn)存取方法
如果@synthesize和@dynamic都沒(méi)寫(xiě)嘲碧,那么默認(rèn)的就是@syntheszie var = _var;
我們來(lái)看一下屬性的本質(zhì)和進(jìn)化過(guò)程:
原始形態(tài):
? ? 定義一個(gè)實(shí)例變量:int age;
? ? 先在.h文件中聲明setter和getter器
? ? -(void)setAge:(int)newAge;
? ? ?-(int)age;
然后在.m文件中具體實(shí)現(xiàn)
-(void)setAge:(int)newAge
{
? ? ?age=newAge;
}
-(int)age
{
?return?age;
}
這樣的話就可以跟屬性一樣使用了。
//比如上面的聲明是一個(gè)Person類(lèi)
Person*person=[[Person?alloc]init];
[person?setAge:13];
int age=[person?age];
//點(diǎn)調(diào)用
person.age=13;??//.調(diào)用出現(xiàn)在=號(hào)左邊父阻,相當(dāng)于setter
intage=person.age???//.調(diào)用出現(xiàn)在=號(hào)的右邊愈涩,相當(dāng)于getter
NSLog(@"%i",person.age);//這也是getter
setter和getter的改進(jìn)寫(xiě)法:
每次要為一個(gè)屬性寫(xiě)上getter和setter望抽,不得不手十分麻煩,所以有了更簡(jiǎn)單的寫(xiě)法履婉,
在.h文件里煤篙,直接這樣寫(xiě),表示聲明了一個(gè)實(shí)例屬性和它的getter和setter器
@property?int?age;
然后在.m文件中這樣寫(xiě)毁腿,
@synthesize?age;
表示實(shí)現(xiàn)setteer和getter辑奈,這樣,就可以和以前一樣調(diào)用getter和setter了已烤。
setter和getter的改進(jìn)優(yōu)化:
可以看到鸠窗,getter器的方法名直接就是變量名,方法名和變量名一樣胯究,容易讓人迷糊稍计,所以,可以這樣優(yōu)化裕循。
在.h文件中依然這樣聲明
@property?int?age;
在.m文件中臣嚣,這樣去寫(xiě),
@synthesizeage=_age;?//加上一個(gè)_
//這么剥哑,我們就可以去使用_age???和使用age一樣
-(void)show
{
NSLog(@"%i",_age);
}
可以看出來(lái)茧球,在Objective-C中setter器沒(méi)什么區(qū)別,不過(guò)getter器的方法名缺少了get星持,因?yàn)間et...在Objective-C有別的用處抢埋,所以getter器直接寫(xiě)的就是變量名。
? ? ?1. 如果只聲明一個(gè)屬性a督暂,不使用@synthesize實(shí)現(xiàn):編譯器會(huì)使用_a作為屬性的成員變量(如果沒(méi)有定義成員變量_a則會(huì)自動(dòng)生成一個(gè)私有的成員變量_a揪垄;如果已經(jīng)定義了成員變量_a則使用自定義的成員變量_a。注意:如果此時(shí)定義的成員變量不是_a而是a則此時(shí)會(huì)自動(dòng)生成一個(gè)成員變量_a逻翁,它跟自定義成員變量a沒(méi)有任何關(guān)系)饥努;
? ? ? 2.如果聲明了一個(gè)屬性a,使用@synthesize a進(jìn)行實(shí)現(xiàn)八回,但是實(shí)現(xiàn)過(guò)程中沒(méi)有指定使用的成員變量(例如上面birthday):則此時(shí)編譯器會(huì)使用a作為屬性的成員變量(如果定義了成員變量a酷愧,則使用自定義成員變量;如果此時(shí)沒(méi)有定義則會(huì)自動(dòng)生成一個(gè)私有的成員變量a缠诅,注意如果此時(shí)定義的是_a則它跟生成的a成員變量沒(méi)有任何關(guān)系)溶浴;
? ? ? 3.如果聲明了一個(gè)屬性a,使用@synthesize a=_a進(jìn)行實(shí)現(xiàn)管引,這個(gè)過(guò)程已經(jīng)指定了使用的成員變量:此時(shí)會(huì)使用指定的成員變量作為屬性變量士败;(那還不如不寫(xiě)這個(gè)?@synthesize ..)
有了上面的總結(jié),相信理解上面的代碼并不難,通常在實(shí)際開(kāi)發(fā)過(guò)程中我們要么直接在@property中聲明不使用@synthesize谅将;要么使用過(guò)程中指定具體的成員變量漾狼。
此外再次強(qiáng)調(diào)一下,通過(guò)上面的方式定義變量的本質(zhì)還是生成對(duì)應(yīng)的gettter饥臂、setter方法(只是這個(gè)步驟編譯器幫你完成了)逊躁,如果通過(guò)@property定義了屬性,同時(shí)在.m中又自定義實(shí)現(xiàn)了對(duì)應(yīng)方法隅熙,則會(huì)使用自定義方法稽煤。
定義setter的語(yǔ)義
nonatomic??禁止多線程,變量保護(hù)猛们,提高性能念脯。
atomic ? ?是Objc使用的一種線程保護(hù)技術(shù)狞洋,基本上來(lái)講弯淘,是防止在寫(xiě)未完成的時(shí)候被另外一個(gè)線程讀取,造成數(shù)據(jù)錯(cuò)誤吉懊。而這種
機(jī)制是耗費(fèi)系統(tǒng)資源的
assign ? 此標(biāo)記說(shuō)明設(shè)置器直接進(jìn)行賦值 庐橙,賦值特性,不涉及引用計(jì)數(shù)借嗽,弱引用對(duì)基礎(chǔ)數(shù)據(jù)類(lèi)型 (NSInteger态鳖,CGFloat)和C數(shù)據(jù)類(lèi)型(int, float,double, char)等等。
如果你要一個(gè)屬性使用assign恶导,且這個(gè)類(lèi)符合NSCopying協(xié)議.
retain ? ?對(duì)其他NSObject和其子類(lèi)對(duì)參數(shù)進(jìn)行release舊值浆竭,再retain新值,新對(duì)象的引用計(jì)數(shù)+1惨寿;
指定retain會(huì)在賦值時(shí)喚醒傳入值的retain消息邦泄。此屬性只能用于Objective-C對(duì)象類(lèi)型,而不能用于Core
Foundation對(duì)象裂垦。(原因很明顯顺囊,retain會(huì)增加對(duì)象的引用計(jì)數(shù),而基本數(shù)據(jù)類(lèi)型或者Core Foundation對(duì)象都沒(méi)有引用計(jì)數(shù)——譯者注)蕉拢。
注意: 把對(duì)象添加到數(shù)組中時(shí)特碳,引用計(jì)數(shù)將增加對(duì)象的引用次數(shù)+1。
copy ? 對(duì)NSString 它指出晕换,在賦值時(shí)使用傳入值的一份拷貝午乓。拷貝工作由copy方法執(zhí)行闸准,此屬性只對(duì)那些實(shí)行NSCopying協(xié)議的對(duì)象類(lèi)型有效硅瞧,表示兩個(gè)對(duì)象內(nèi)容相同,新的對(duì)象retain為1 恕汇,與舊有對(duì)象的引用計(jì)數(shù)無(wú)關(guān)腕唧。
weak? ? weak比assign多了一個(gè)功能或辖,當(dāng)對(duì)象消失后自動(dòng)把指針變成nil,好處不言而喻枣接。弱引用除了不決定對(duì)象的存亡外颂暇,其他與強(qiáng)引用相同。即使一個(gè)對(duì)象被持有無(wú)數(shù)個(gè)若引用但惶,只要沒(méi)有強(qiáng)引用指向他耳鸯,那麼其還是會(huì)被清除。
retain? ?是指針拷貝膀曾,copy 是內(nèi)容拷貝县爬。
iOS 5 中對(duì)屬性的設(shè)置新增了strong 和weak關(guān)鍵字來(lái)修飾屬性(iOS 5 之前不支持ARC)
strong ? 用來(lái)修飾強(qiáng)引用的屬性,一塊內(nèi)存(一個(gè)對(duì)象)當(dāng)沒(méi)有 strong 類(lèi)型的指針指向它時(shí),它就會(huì)被釋放添谊;ARC中使用财喳,與MRC中retain同義,使用之后斩狱,計(jì)數(shù)器+1耳高。
官方文檔中有這樣的示例代碼:// The following declaration is a synonym for: @property(retain) MyClass *myObject;@property(strong) MyClass *myObject;表示了strong和retain是同義詞。
weak? ? 用來(lái)修飾弱引用的屬性所踊,當(dāng)一塊內(nèi)存(一個(gè)對(duì)象)被釋放時(shí)泌枪,指向它的 weak 類(lèi)型指針就會(huì)被釋放并賦值為 nil。
所以秕岛,如果一般情況下碌燕,我們都不希望字串的值跟著str變化,所以我們一般用 copy 來(lái)設(shè)置string的屬性继薛。如果希望字串的值跟著賦值的字串的值變化修壕,可以使用 strong,retain惋增。當(dāng)然這也只是針對(duì) NSMutableString叠殷,因?yàn)槿绻?NSString 那么 copy 與 retain 的效果是一樣的。
這里需要提一下诈皿,如果一個(gè)對(duì)象作為屬性時(shí)林束,定義setter的語(yǔ)義使用了copy字段,那么需要遵循 <NSCopying>協(xié)議稽亏,并且需要重寫(xiě) copyWithZone 方法壶冒。否則會(huì)cash。
NSZone 是蘋(píng)果對(duì)內(nèi)存分配和釋放的優(yōu)化方式截歉。NSZone不是一個(gè)對(duì)象胖腾;它是一個(gè)難懂的C結(jié)構(gòu),它被用于紀(jì)錄關(guān)于內(nèi)存處理(管理)一系列對(duì)象的信息。
- (id)copyWithZone:(NSZone*)zone
{
? ? id ? copy = [[[self ?class]alloc]init];
? ?if(copy){
? ? // self.imageName 是 當(dāng)前的屬性
? ? ?[copy ? setImageName:[self.imageName copyWithZone:zone]];
? ?}
? ? ? ?return ? copy;
}
一些需要注意的知識(shí)
1.實(shí)例方法/動(dòng)態(tài)方法
2.靜態(tài)方法/類(lèi)方法
靜態(tài)方法在堆上分配內(nèi)存(釋放工作由程序員控制)咸作,實(shí)例方法在棧上(是由編譯器自動(dòng)管理)
靜態(tài)方法常駐內(nèi)存锨阿,實(shí)例方法不是,所以靜態(tài)方法效率高但占內(nèi)存
本文參考文章: