寫這篇博客是因?yàn)榕紶柨吹揭黄恼旅杏椋唧w我忘了绿聘。大意就是說找一個(gè)靠譜的iOS開發(fā)人員,問他一個(gè)問題@property'的用法办绝,就能大致知道他的水平伊约。一開始我認(rèn)為這是一個(gè)很低級(jí)的問題。然后我看了他大致看了一下他的問題孕蝉。然后就有了接下來這篇文章屡律。
首先我們創(chuàng)建一個(gè)CLT工程。然后創(chuàng)建一個(gè)ClassA降淮,在.h文件中添加如下代碼
@interface ClassA : NSObject
{
int _num;
char _chr;
}
@end
如上所示,我們定義兩個(gè)成員變量.最近兩年學(xué)習(xí)iOS朋友估計(jì)對(duì)這種寫法比較陌生,莫慌,切聽我慢慢道來.
首先我們將這個(gè)聲明在main函數(shù)使用一下,大家就會(huì)發(fā)現(xiàn)其實(shí)這個(gè)時(shí)候我們的實(shí)例對(duì)象時(shí)不能訪問這兩個(gè)成員變量.
](http://upload-images.jianshu.io/upload_images/967672-3e1852c1847b2733.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
這個(gè)時(shí)候有沒有想說一句,既然不能訪問,我們聲明他有神馬意義,完全沒用啊.原因是神馬那.
這句話原因應(yīng)該很清楚了,這個(gè)時(shí)候我們發(fā)現(xiàn)原來,實(shí)例變量是protected屬性,原則上是不能訪問的.這時(shí)候有過面向?qū)ο笞兂傻呐笥褧?huì)說,改成public屬性就可以了.沒錯(cuò),改為public屬性的確可以解決,但是這不是我今天要說的重點(diǎn),有興趣的朋友可以去嘗試一下.
我們大家都應(yīng)該知道面向?qū)ο蟮娜筇卣?封裝,繼承,多態(tài).既然OC是一門面向?qū)ο缶幊痰恼Z言,我們我應(yīng)該能夠想到用面向?qū)ο笳Z言的特性去解決.沒錯(cuò),就是用數(shù)據(jù)的封裝去解決.有過編程經(jīng)驗(yàn)的朋友應(yīng)該聽說過set,get方法(好像也叫屬性訪問控制器),他們是面向?qū)ο缶幊陶Z言慣用的實(shí)例變量封裝方法.形式如下:
- (void)setNum:(int)num;
- (int)num;
上面兩個(gè)方法如果看不懂,兄弟,證明你學(xué)的有些問題,而且還不小.在這里我不跟大家詳談,如果不懂得朋友,可以看一些關(guān)于面向?qū)ο蟮臅?至于實(shí)現(xiàn),很簡(jiǎn)單,不過我還是講他寫出來.
- (void)setNum:(int)num {
_num = num;
}
- (int)num {
return _num;
}
為什么我們這樣寫,我簡(jiǎn)單也跟大家提一句,其實(shí)看到這兩個(gè)函數(shù),大家應(yīng)該都明白,提示我們就是講原來通過實(shí)例變量直接訪問實(shí)例變量屬性變成通過公共方法去訪問實(shí)例變量的屬性.
大家肯定會(huì)很納悶,今天你不是要說property,為啥主角到現(xiàn)在都沒出場(chǎng)那.ok,現(xiàn)在馬上出場(chǎng).現(xiàn)在我們注釋掉原來的聲明方式.采用property聲明
這個(gè)時(shí)候
這個(gè)時(shí)候會(huì)出現(xiàn)以下情況:
1.在main函數(shù)中可以通過set,get方法訪問成員變量,但是不能直接訪問.
2.在類中可以直接訪問到相應(yīng)的實(shí)例變量.
這個(gè)時(shí)候我們?cè)趤韯?chuàng)建另外一個(gè)類,我們讓個(gè)這個(gè)類繼承與這個(gè)類,我們實(shí)現(xiàn)是一個(gè)共有方法,然后輸出num的值,這個(gè)時(shí)候我們會(huì)發(fā)現(xiàn)我們可以通過set和get方法可以訪問到num,但是不能直接通過變量名訪問.
通過以上的實(shí)際操作我們得出以下結(jié)論.
property聲明的變量屬性不是public.
property聲明的變量會(huì)自動(dòng)生成相應(yīng)的實(shí)例變量,set,get方法
在這里再跟大家多說一點(diǎn)超埋,估計(jì)也有很多見到過這個(gè)東西。就是如下的形式
@synthesize num = _num;
對(duì)于上面這行代碼佳鳖,《objective-c 2.0程序編程》有明確的說明霍殴,編譯器會(huì)自動(dòng)生成相應(yīng)的set,get方法系吩,并且與成員變量(_變量名)相關(guān)聯(lián)来庭。
話說如果到了這里你就覺得@property的使用就結(jié)束了,那我只能說一句穿挨,年輕人月弛,現(xiàn)在知識(shí)個(gè)開頭而已。言歸正傳科盛,
2property的內(nèi)存管理
說到這里就要說下事例變量的屬性了(其實(shí)就是括號(hào)里面的內(nèi)容)帽衙。說明一下,變量的屬性主要是和內(nèi)存管理有關(guān)贞绵,強(qiáng)調(diào)一下厉萝,是主要是內(nèi)存管理有關(guān),不是全部
atomic(原子性) vs nonatomic(非原子性)【線程安全】
這主要是針對(duì)與多線程編程環(huán)境下來說的。怎么說那冀泻,假設(shè)我們現(xiàn)在多線程環(huán)境編程中常侣,如果同時(shí)又兩個(gè)或者兩個(gè)以上的線程同時(shí)對(duì)一個(gè)屬性就行賦值,這個(gè)時(shí)候?yàn)榱吮WC屬性調(diào)用前后的一致性弹渔,我們通常要做些多余的事胳施,這就是傳說中的線程安全。也就是說線程安全就是為了保證在多線程環(huán)境中肢专,有且僅有一個(gè)線程能對(duì)當(dāng)前屬性進(jìn)行set和get操作舞肆,其他線程必須等待其完成操作之后再進(jìn)行操作。
回到咱們主題上博杖,Objective-C中原子性就是為了保證線程安全而存在椿胯。如果當(dāng)前的某一屬性的屬性為原子性,那么任何一個(gè)線程對(duì)其記性set和get方法時(shí)都會(huì)對(duì)當(dāng)前的屬性進(jìn)行加鎖和解鎖操作(Objective-C中保證線程安全的一種方法)剃根。從而保證其在多線程編程環(huán)境的線程安全哩盲。通常情況下,我們不會(huì)涉及過多的線程安全狈醉,并且加鎖和解鎖操作也會(huì)造成相當(dāng)多的資源開銷廉油,所以我們一般都將屬性設(shè)置為非原子性。但是蘋果公司為了安全考慮出發(fā)苗傅,默認(rèn)情況下抒线,這個(gè)屬性是非原子性
readwrite(讀寫) vs readonly (只讀) 【訪問控制】
這兩個(gè)屬性相對(duì)還是比較好理解的。這屬性默認(rèn)情況下是讀寫的渣慕,這就是為什么我們可以對(duì)實(shí)例變量進(jìn)行取值和賦值的操作嘶炭,其實(shí)質(zhì)就是有set和get方法。通過這個(gè)說明相信聰明的你已經(jīng)猜到只讀的含義和實(shí)質(zhì)了逊桦。只讀的含義大家用心領(lǐng)悟一下眨猎,在這里我我說一下。其實(shí)就是就是只寫了get方法卫袒,沒有提供get方法宵呛。到這里不知道大家有沒有想過一個(gè)問題。為啥沒有只寫方法夕凝,想了半天,突然發(fā)現(xiàn)户秤,只寫有不能讀有啥用啊
另外如果你不喜歡編譯器默認(rèn)的set和get方法码秉,你可以自定義set和get方法,聲明如下
@property (getter=isRunning, readonly) BOOL running;
為了操作的方便鸡号,蘋果屬性將讀寫屬性設(shè)置為實(shí)例變量的默認(rèn)屬性
接下來是今天要說的重頭戲
strong (強(qiáng)引用) vs weak(弱引用) vs assign(賦值) vs copy(復(fù)制)
每一遍編程語言都無法繞開的深淵--內(nèi)存管理转砖。特別是在移動(dòng)設(shè)備上這種內(nèi)存資源相對(duì)短缺的設(shè)備上。當(dāng)然這個(gè)也是有相對(duì)來說的,比如現(xiàn)在Android手機(jī)最大的運(yùn)行內(nèi)存已經(jīng)可達(dá)到4G府蔗,呵呵噠...... 但是就目前iOS設(shè)備來說晋控,內(nèi)存資源還是比較稀缺的(貌似iPhone 7要擴(kuò)容了)。
在大多數(shù)的面向?qū)ο蟮恼Z言中都是通過垃圾回收姓赤,但是蘋果公司開發(fā)一套我認(rèn)為相當(dāng)流b的機(jī)制--對(duì)象擁有關(guān)系(object ownership)赡译。專有的名詞沒找到,我就強(qiáng)行秀一波英語不铆。大概就是說蝌焚,當(dāng)我們對(duì)一個(gè)對(duì)象就行操作時(shí),我們必須確定他的存在誓斥,就是說我們必須擁有它只洒。如果當(dāng)前對(duì)象沒有擁有者,那么操作系統(tǒng)(不是編譯器)會(huì)在一個(gè)合適的時(shí)間(目前我也不確定劳坑,有種說法是兩個(gè)runloop切換期間)將其銷毀并釋放掉其所占內(nèi)存毕谴。這個(gè)機(jī)制的實(shí)現(xiàn)依賴于ARC機(jī)制。至于ARC機(jī)制在這里我不多講距芬,有一點(diǎn)跟大家說明析珊,當(dāng)對(duì)象擁有者增加時(shí),當(dāng)前對(duì)象的引用計(jì)數(shù)會(huì)+1蔑穴,如果引用計(jì)數(shù)為0時(shí)忠寻,就是沒有對(duì)象擁有,那么這個(gè)對(duì)象就可能被釋放存和。
strong屬性就是表示我擁有當(dāng)前對(duì)象奕剃,并且當(dāng)前對(duì)象的引用計(jì)數(shù)+1.strong有一個(gè)好處就是說,我能夠確認(rèn)當(dāng)前對(duì)象的存在捐腿,因?yàn)橹灰也幌ё菖螅?dāng)前對(duì)象就不會(huì)被銷毀,即不會(huì)消失茄袖。這就保證了我能夠在需要的時(shí)候隨時(shí)訪問當(dāng)前這個(gè)對(duì)象操软。
通過上面的介紹大家應(yīng)該能推出來如果要釋放一個(gè)對(duì)象,必須是他的引用計(jì)數(shù)(擁有者的數(shù)量)為0.好大家看下面這張圖
簡(jiǎn)單說明一下宪祥,對(duì)象A強(qiáng)應(yīng)用對(duì)象B,同時(shí)對(duì)象B也強(qiáng)引用對(duì)象A聂薪,這時(shí)候大家想象一下,當(dāng)我程序執(zhí)行結(jié)束后蝗羊,操作系統(tǒng)會(huì)怎樣釋放這兩個(gè)對(duì)象那藏澳,因?yàn)槌绦驁?zhí)行過程中,兩個(gè)對(duì)象的引用計(jì)數(shù)都不可能為0耀找,因而都不會(huì)被釋放翔悠,但是當(dāng)程序執(zhí)行結(jié)束,會(huì)怎樣?沒錯(cuò)蓄愁,這個(gè)就是經(jīng)典的循環(huán)引用計(jì)數(shù)問題双炕,而他的直接后果就是鼎鼎大名的“內(nèi)存泄漏”。當(dāng)程序執(zhí)行完成之后撮抓,操作系統(tǒng)不會(huì)釋放掉任何一方妇斤,從而導(dǎo)致兩者一直留在內(nèi)存中,導(dǎo)致我們的內(nèi)存越來越下胀滚。
很明顯這個(gè)不是我們想要的趟济,但是以上的情況和多時(shí)候我們又無法避免。為了解決這個(gè)問題咽笼,另外一個(gè)屬性被發(fā)明出來顷编,他就說weak,同樣咱們看一下模態(tài)圖
簡(jiǎn)單說明剑刑,對(duì)象A此時(shí)仍然強(qiáng)引用對(duì)象B媳纬,而對(duì)象B弱引用對(duì)象A。細(xì)心同學(xué)發(fā)現(xiàn)此時(shí)對(duì)象B擁有對(duì)象A為虛線施掏,并且對(duì)象A的引用計(jì)數(shù)并沒有增加钮惠。或許你會(huì)想是不是畫錯(cuò)了七芭。我可以告訴NO素挽,這這你weak的精髓。簡(jiǎn)單明了狸驳,當(dāng)B釋放的時(shí)候预明,我的兩個(gè)對(duì)象都會(huì)被釋放掉,并且當(dāng)前對(duì)象A的指針會(huì)被置為nil。弱引用的實(shí)質(zhì)就是和strong一樣擁有當(dāng)前對(duì)象耙箍,能夠?qū)ο螽?dāng)前對(duì)象進(jìn)行操作撰糠,但是不使其引用計(jì)數(shù)增加。這樣就完美的解決了循環(huán)引用問題辩昆。給他家提個(gè)醒阅酪,不要亂用weak,可能會(huì)導(dǎo)致奇怪的bug汁针。代理協(xié)議的實(shí)例對(duì)象通常設(shè)置為weak屬性
至于copy這個(gè)屬性可能是有人覺得比較茫然术辐。如果一個(gè)屬性被設(shè)置為copy屬性時(shí),對(duì)象記性讀寫操作扇丛,他獲得的是當(dāng)前的對(duì)象的一份copy术吗,而不是簡(jiǎn)單對(duì)其進(jìn)行應(yīng)用。并且源對(duì)象的引用計(jì)數(shù)不會(huì)增加帆精。通常經(jīng)常下copy屬性主要用于源對(duì)象可能發(fā)生改變,而不像當(dāng)前對(duì)象受其影響。不如說我們將一個(gè)NSMutableString類型的String賦值給NSString類型的對(duì)象卓练,這個(gè)時(shí)候我們?yōu)榱朔乐剐薷腘SMutableString對(duì)象對(duì)當(dāng)前對(duì)象影響隘蝎。我們會(huì)考慮將其設(shè)置為copy。通常情況string對(duì)象和block對(duì)象會(huì)被設(shè)置為copy屬性襟企。
總結(jié)一下嘱么,以上三個(gè)屬性都是針對(duì)于對(duì)象來說的,但是大家都知道Objective-C不是一門新的編程語言顽悼,它只是在C語言的基礎(chǔ)上加上了一層面向?qū)ο蟮奶匦月瘛D敲磫栴}來了,C語言中有好多基礎(chǔ)類型蔚龙,聲明這些屬性時(shí)我們?cè)鯓釉O(shè)置的他的屬性冰评?別急,不還有一個(gè)嗎木羹?assign主要用來修飾Objective-C中基礎(chǔ)屬性甲雅。Objective-C支持64位以后全面更新了基礎(chǔ)屬性的定義。其實(shí)就是做了一些兼容64的修改坑填,例如int -> NSInteger等抛人。
當(dāng)然還有一些其他的屬性,比如unsafe_unretained脐瑰,這個(gè)跟weak有很多相似妖枚,當(dāng)時(shí)不同的是他不會(huì)將當(dāng)前的對(duì)象指針置為nil。如果你需要使用他苍在,請(qǐng)確定當(dāng)前環(huán)境不支持weak绝页,因?yàn)閺乃志椭肋@貨不是很好。哈哈哈忌穿。
當(dāng)然以上的所有都是針對(duì)于ARC機(jī)制下來說抒寂,對(duì)于老的開發(fā)程序員(就是MRC下開發(fā)的前輩們),還有retain等屬性掠剑。在這里我就不再展開來說屈芜。我表示我趕上了MRC的尾巴,但是當(dāng)時(shí)沒有研究這個(gè)東東朴译,感覺太坑了井佑。就直接跳進(jìn)了ARC的懷抱。現(xiàn)在想想眠寿,還是真是幸福啊躬翁。哈哈
參考文獻(xiàn):
《Objective-C 2.0程序設(shè)計(jì)》 (美)Steophen G.Kochan 著
Ry`s Cocoa Tutorial http://rypress.com/tutorials/objective-c/properties
Objective-C Property Attributes https://realm.io/news/tmi-objective-c-property-attributes/
iOS-Blog http://www.ios-blog.co.uk/tutorials/objective-c/synthesize-vs-dynamic/
StackOverflow1 http://stackoverflow.com/questions/5170631/what-does-synthesize-window-window-do
StackOverflow2 http://stackoverflow.com/questions/8927727/objective-c-arc-strong-vs-retain-and-weak-vs-assign