第七章 屬性宣告

陳振發表於2017-12-13

使用屬性程式設計:

一般來說,屬性指的是一個物件的屬性或特性。物件的例項變數,也就是訪問方法的目標一般被稱為屬性。

以前的介面檔案中使用例項變數和訪問方法實現屬性的概念,而現在把屬性的概念作為一個獨立的存在在介面中宣告。

屬性宣告的規則總結如下:

自動生成訪問方法

自動生成例項變數

更簡單的呼叫訪問方法

屬性的內省

屬性的概念:

使用屬性宣告,可以更簡潔地實現訪問方法。另一方面,不僅僅是訪問方法,KVC中所有定義的例項變數都可以被當作屬性處理。

顯式宣告屬性:

OC2.0中新增加了屬性宣告的功能。這個功能可以讓編譯器自動生成與資料成員同名的方法。

@property int hitPoint;

屬性宣告等同於宣告瞭讀寫兩個訪問方法。

屬性宣告的時候還可以為屬性自定義選項。選項位於圓括號中,前面是@property指令。例如如果想宣告一個只讀的訪問方法,可以在@property後面加上(readonly)。

@property(readonly)  NSString *name;

當兩個屬性的型別相同時,既可以單獨寫一行,也可以將他們寫一起。例如:

@property int hitPoint, magicPoint;

屬性的實現:

通過使用@synthesize,就可以在一行之內自動生成getter和setter方法。將語句應放在@implementation和@end之間,就能自動生成和介面檔案中宣告的屬性一致的訪問方法(可讀可寫或只讀)。也可以不使用@synthesize自動生成,而是由自己來實現訪問方法。另外我們還可以通過@dynamic關鍵字告訴編譯器合成無效,使用者會自己生成getter和setter。

其他方法可以直接在實現檔案中實現,而不用在介面檔案中宣告。但是屬性宣告的情況下則不允許這種做法。

使用@synthesize的時候,可以在一行中宣告多個變數。

通常情況下,@property宣告的屬性名稱和例項變數的名稱是相同的,但有時你也可能會需要屬性的名稱和例項變數的名稱不同,這時就可以為例項變數定義其他的屬性名稱。例如我們可以通過該語句生成名為value的訪問方法,並將其繫結到例項變數runningAverage中:

@synthesize value = runningAverage;

可以在類的實現部分中宣告一部分或全部例項變數,這種宣告方法可以隱藏是否對變數進行了屬性宣告。另外在子類中訪問例項變數時也只能通過訪問方法來訪問,不能直接訪問父類的例項變數。

通過屬性宣告的方法也能夠同訪問方法一樣實現封裝的目的。

給屬性指定選項:

可以同時給一個變數指定多個選項,選項之間需要用逗號隔開。

@property 可用選項:

種類

選項

說明

指定方法名

getter = getter方法名

setter = setter方法名顯式指定getter方法和setter方法的名字

讀寫屬性

readonly

readwrite只讀

讀寫

賦值時的選項

assign

retain

unsafe_unretained

strong

weak

copy單純賦值

進行保持操作

同assign一樣(用於ARC)

同retain一樣(用於ARC)

弱引用(用於ARC)

複製物件

原子性操作

nonatomic

非原子性操作,非執行緒安全

指定方法名:

可以不使用預設的訪問方法名,而通過setter option來指定訪問屬性用的方法名。例:

@property(setter = setValue)int hitPoint;

可以通過點運算子來呼叫 .hitPoint,但實際上啟動的方法是setValue

讀寫屬性:

readwrite表示屬性是可讀寫的,這也是預設選項。

賦值時的選項:

六個選項之間是排他關係。unsafe_unretained和strong主要被用在ARC的情況下,分別和assign和retain具備同樣的功能。

基礎資料型別

物件型別:手動引用計數

物件型別:ARC

物件型別:垃圾回收

未指定任何選項

直接賦值

警告

警告

直接賦值(有可能出現警告)

assign

unsafe_unretained直接賦值

直接賦值

直接賦值

直接賦值

retain

strong出錯

賦值並對新值進行retain

賦值並對新值進行retain

無特別操作,和assign動作相同

weak

出錯

無特別操作,和assign動作相同

弱引用

無特別操作,和assign動作相同

copy

出錯

賦值時建立傳入值的一份副本

賦值時建立傳入值的一份副本

賦值時建立傳入值的一份副本

屬性是物件型別,且使用了垃圾回收管理記憶體,有一點需要注意,對於符合NSCopying協議也就是說可以利用copy方法的類例項變數,如果不指定任何選項,就會提示警告。

原子性:

原子性是多執行緒中的一個概念,如果說訪問方法是原子的,那就意味著多執行緒環境下訪問屬性是安全的在執行過程中不可被打斷。而nonatomic則正好相反,意味著方法在執行時可被打斷,預設情況下訪問方法是原子的。

通常不需要指定nonatomic選項,因為這樣的機制能提高訪問的安全性。但畢竟lock和unlock操作對效能有影響,因此,對於使用頻繁且不用考慮多執行緒競爭的訪問方法可以在宣告的時候加上nonatomic。

nonatomic選項不僅能被用於使用@synthesize生成的訪問方法,手動定義的訪問方法中不存在多執行緒競爭的情況下,也可以給屬性加上nonatomic。

屬性宣告和繼承:

子類可以使用父類中定義的屬性,也可以重寫父類中定義的訪問方法,但是,父類中屬性宣告時指定的各種屬性(assign等),或者為例項變數指定的getter和setter的名稱等必須完全一樣。唯一一個特別的情況是,對於父類中被定義為readonly型別的屬性,子類中可以將其變為readwrite。

屬性的宣告可能會包含範疇或協議,這種情況下實現檔案中不可以使用@synthesize,原因是範疇和協議都和例項變數的實現無關,需要在實現檔案中實現訪問方法。

方法族和屬性的關係:

使用ARC的時候,必須注意方法的命名,不要和方法族發生衝突。

點操作符的使用方法:

OC2.0會在編譯時把使用點操作符訪問屬性的過程理解為訪問方法的呼叫,因為呼叫的是訪問方法,所以無論對應的例項變數是否存在,只要訪問方法存在,就都可以通過點操作符訪問屬性。

點操作符只能用於類型別的例項變數,不能對id型別的變數應用點操作符。因為沒指定型別的情況下,編譯器無法判斷是否存在屬性對應的訪問方法。

複雜點操作符的使用方法:

連用點操作符:

當一個物件的例項變數是另外一個物件時,可用過連用點操作符來訪問物件的例項變數中的成員。如果連用表示式中有一個是nil,則整個表示式的返回值就是nil。

連續賦值:

賦值時從右向左解釋。

對遞增,遞減和複合賦值運算子的解釋:

e = obj.depth++;

賦值表示式的右側連續呼叫了getter和setter方法,相當於執行了[ obj setDepth: [ obj depth ] + 1 ]。最後為e賦值的是遞增操作之前的depth的值。

self使用點操作符:

類的方法中可以通過對self應用點操作符來呼叫自己的訪問方法。但要注意的是,不要在訪問方法中使用self,否則會造成無限迴圈的遞迴,無法終止。

super使用點操作符:

可以通過給super加點操作符來呼叫父類中定義的setter和getter方法。例如:

- (void)setDepth:(int

)val {

super

.depth = (val <= maxDepth) ? val : maxDepth;

}

和構造體成員混用:

獲取類屬性的點操作符和訪問結構體元素的點操作符可以混用。不能通過取地址符來對點操作符獲得的屬性取地址。

當給obj的例項變數contents傳送訊息時,你可能會這樣寫:[ obj.contents retain ]  。但要注意的是,實際上這行語句表示的是給getter方法的返回值傳送了訊息,並不一定會給obj的例項變數contents傳送訊息。

使用點操作符訪問物件的例項變數和C語言中使用點操作符訪問結構體的成員意義是不一樣的。訪問物件的例項變數的最正統的方法是通過 -> 操作符來訪問。編譯器在碰到點操作符的時候並沒有直接訪問例項變數而是呼叫了訪問方法。

何時使用點操作符:

沒有引數的方法,無論其是不是和屬性相關,都可以通過點操作符來呼叫。但原則上還是隻對屬性宣告中定義的屬性應用點操縱符。

使用點操作符會帶來呼叫方法的負擔,影響效能。

嚴格來說,使用依賴於實現的方式來訪問例項變數是不允許的,所以應避免直接訪問例項變數。但屬性對應的訪問方法則一定要直接訪問例項變數。

此外,在初始化方法中通過點操作符訪問屬性的時候要注意,因為初始化方法執行的時候這個例項還沒完成初始化,屬性對應的訪問方法有可能還沒生成。

相關文章