在iOS開(kāi)發(fā)過(guò)程中,我們用@proprety聲明一個(gè)屬性后,在代碼中我們可以用self.xx與_xx來(lái)獲取到這個(gè)屬性。但是一直有一個(gè)疑惑甘磨,那就是這兩個(gè)之間有什么區(qū)別呢?最初我一直覺(jué)得這兩個(gè)之間沒(méi)什么區(qū)別的熟呛,直到有一次宽档,我發(fā)現(xiàn)自己明明對(duì)聲明的屬性進(jìn)行了賦值尉姨,但是在使用_xx引用時(shí)發(fā)現(xiàn)為nil庵朝,這才引起我的注意。所以又厉,今天在這里對(duì)這個(gè)問(wèn)題進(jìn)行統(tǒng)一的一個(gè)說(shuō)明和學(xué)習(xí)九府。
1 @property 與 @synthesize
在說(shuō)self.xx與_xx之前,我們先了解一下@property 以及 @synthesize之間的區(qū)別和聯(lián)系覆致,說(shuō)到@property 以及 @synthesize侄旬,我們就不得不提到iOS中 成員變量和屬性 之間的區(qū)別和聯(lián)系了。
接觸iOS的人都知道煌妈,@property聲明的屬性默認(rèn)會(huì)生成一個(gè)_類型的成員變量儡羔,同時(shí)也會(huì)生成setter/getter方法。但這只是在iOS5之后璧诵,蘋果推出的一個(gè)新機(jī)制汰蜘。看老代碼時(shí)之宿,經(jīng)匙宀伲看到一個(gè)大括號(hào)里面定義了成員變量,同時(shí)用了@property聲明比被,而且還在@implementation中使用@synthesize方法色难,就像下面的代碼這樣:
其實(shí)泼舱,發(fā)生這種狀況根本原因是蘋果將默認(rèn)編譯器從GCC轉(zhuǎn)換為L(zhǎng)LVM(low level virtual machine),才不再需要為屬性聲明實(shí)例變量了枷莉。在沒(méi)有更改之前娇昙,屬性的正常寫法需要**成員變量 + @property + @synthesize 成員變量 **三個(gè)步驟。
如果我們只寫成員變量+ @property:
但更換為L(zhǎng)LVM之后依沮,編譯器在編譯過(guò)程中發(fā)現(xiàn)沒(méi)有新的實(shí)例變量后涯贞,就會(huì)生成一個(gè)下劃線開(kāi)頭的實(shí)例變量。因此現(xiàn)在我們不必在聲明一個(gè)實(shí)例變量危喉。(注意:==是不必要宋渔,不是不可以==)當(dāng)然我們也熟知,@property聲明的屬性不僅僅默認(rèn)給我們生成一個(gè)_類型的成員變量辜限,同時(shí)也會(huì)生成setter/getter方法皇拣。在.m文件中,編譯器也會(huì)自動(dòng)的生成一個(gè)成員變量_myString薄嫡。那么在.m文件中可以直接的使用_myString成員變量氧急,也可以通過(guò)屬性self.myString.都是一樣的。注意這里的self.myString其實(shí)是調(diào)用的myString屬性的setter/getter方法毫深。
此外吩坝,如果我們?cè)僮钚碌拇a中聲明一個(gè)成員變量,如下代碼所示哑蔫,那么我們只是聲明了一個(gè)成員變量钉寝,并沒(méi)有setter/getter方法。所以訪問(wèn)成員變量時(shí)闸迷,可以直接訪問(wèn)name嵌纲,也可以像C++一樣用self->name來(lái)訪問(wèn),但絕對(duì)不能用self.name來(lái)訪問(wèn)腥沽。
從Xcode4.4以后逮走,即iOS的@property已經(jīng)獨(dú)攬了@synthesize的功能主要有三個(gè)作用:
生成了成員變量get/set方法的聲明
生成了私有的帶下劃線的的成員變量因此子類不可以直接訪問(wèn),但是可以通過(guò)get/set方法訪問(wèn)今阳。那么如果想讓定義的成員變量讓子類直接訪問(wèn)那么只能在.h文件中定義成員變量了师溅,因?yàn)樗J(rèn)是@protected
生成了get/set方法的實(shí)現(xiàn)
值得注意的是:
**如果已經(jīng)手動(dòng)實(shí)現(xiàn)了get和set方法(兩個(gè)都實(shí)現(xiàn))的話Xcode不會(huì)再自動(dòng)生成帶有下劃線的私有成員變量了 **
因?yàn)閤Code自動(dòng)生成成員變量的目的就是為了根據(jù)成員變量而生成get/set方法的,但是如果get和set方法缺一個(gè)的話都會(huì)生成帶下劃線的變量
2 self.xx與_xx
上面我們說(shuō)到了屬性與成員變量盾舌、@property 以及 @synthesize之間的聯(lián)系與區(qū)別墓臭。同時(shí),我們提到了self.xx和_xx的一點(diǎn)區(qū)別矿筝,其中self.xx是調(diào)用的xx屬性的get/set方法起便,而_xx則只是使用成員變量_xx,并不會(huì)調(diào)用get/set方法。兩者的更深層次的區(qū)別在于榆综,通過(guò)存取方法訪問(wèn)比直接訪問(wèn)多做了一些其他的事情(例如內(nèi)存管理妙痹,復(fù)制值等),例如如果屬性在@property中屬性的修飾符有retain鼻疮,那么當(dāng)使用self.xx的時(shí)候相應(yīng)的屬性的引用計(jì)數(shù)器由于生成了setter方法而進(jìn)行加1操作怯伊,此時(shí)的retaincount為2。
擴(kuò)展:很多人覺(jué)得OC中的點(diǎn)語(yǔ)法比較奇怪判沟,實(shí)際是OC設(shè)計(jì)人員有意為之耿芹。
點(diǎn)表達(dá)式(.)看起來(lái)與C語(yǔ)言中的結(jié)構(gòu)體訪問(wèn)以及java語(yǔ)言匯總的對(duì)象訪問(wèn)有點(diǎn)類似,如果點(diǎn)表達(dá)式出現(xiàn)在等號(hào)=左邊挪哄,調(diào)用該屬性名稱的setter方法吧秕。如果點(diǎn)表達(dá)式出現(xiàn)在=右邊,調(diào)用該屬性名稱的getter方法迹炼。
OC中點(diǎn)表達(dá)式(.)其實(shí)就是調(diào)用對(duì)象的setter和getter方法的一種快捷方式砸彬,self.myString = @"張三";實(shí)際就是[self setmyString:@"張三"];
最后說(shuō)一下容易出現(xiàn)的問(wèn)題的地方,根據(jù)我個(gè)人的經(jīng)驗(yàn)斯入,
最容易出問(wèn)題的地方就是對(duì)屬性xx或成員變量_xx的初始化的地方和調(diào)用時(shí)機(jī)砂碉,直接通過(guò)例子來(lái)看,我們將屬性和實(shí)例變量的初始化放在重寫的get方法中刻两,于是我們?cè)? (void)viewDidLoad中使用_invoiceInfoImageView來(lái)進(jìn)行布局時(shí)增蹭,實(shí)際上因?yàn)樵谶@之前也沒(méi)有調(diào)用invoiceInfoImageView的get方法,所以此時(shí)invoiceInfoImageView的值其實(shí)為nil磅摹,界面上是空白的滋迈。
如果我們?cè)?使用self.xx來(lái)調(diào)用變量,則會(huì)調(diào)用invoiceInfoImageView的get方法偏瓤,進(jìn)行初始化杀怠,界面布局將會(huì)顯示我們想要的圖片椰憋。此外厅克,如果我們?cè)偈褂胈xx之前用self.xx調(diào)用過(guò)變量invoiceInfoImageView,則同樣會(huì)調(diào)用其get方法從而觸發(fā)invoiceInfoImageView的初始化橙依,這樣也不會(huì)影響布局证舟。
還有一點(diǎn)值得注意的就是我們前面提到過(guò)的,如果我們同時(shí)手動(dòng)重寫了一個(gè)屬性的get和set方法的話窗骑,Xcode不會(huì)再自動(dòng)生成帶有下劃線的私有成員變量了女责。如下圖所示,在我們只定義了get方法時(shí)一切都沒(méi)有問(wèn)題创译,但是一旦我們又重寫set方法抵知,會(huì)發(fā)現(xiàn)用到_xx的地方就會(huì)報(bào)錯(cuò)。
轉(zhuǎn)發(fā)參考文章: