Effective Objective-C 2.0 第6條:理解“屬性”

用OC等面嚮對象語言編程時,“對象”就是“基本構造單元”,在對象之間傳遞數據並執行任務的過程就叫做“消息傳遞”。需要熟悉這兩個特性的工作原理。

屬性

實例變量存取

實例變量一般通過“存取方法”來訪問,其中“獲取方法”(getter)用於讀取變量值,“設置方法”(setter)用於寫入變量值。是OC 2.0裏的“屬性”概念。
點語法是爲了調用屬性。

@interface EOCPerson : NSObject {
@public
    NSString* _firstName;
    NSString* _lastName;
@private
    NSString* _someInternalData;
}

問題:對於OC而言,對象佈局在編譯器就已經很穩固了,只要碰到訪問_firstName變量的代碼,編譯器就把其替換爲“偏移量”,這個偏移量就是硬編碼,表示該變量距離存放對象的內存區域的起始地址有多遠。若又加了一個實例變量,那就麻煩了。

@interface EOCPerson :NSObject {
@public
    NSDate* _dateOfBirth;
    ...
}

原來指向_firstName的偏移量現在卻指向了_dateOfBirth。運行期出現不一致的現象。
OC對於處理此種問題,做法是吧實例變量當做一種存儲偏移量所用的“特殊變量”,交由“類對象”保管。偏移量會在運行期查找,如果類的定義變了,那麼存儲的偏移量也變化了,無論何時訪問實例變量,總能訪問到正確的偏移量。這就是穩固的“應用程序二進制接口”(ABI)。

屬性存取

@interface EOCPerson : NSObject
@property NSString* firstName;
@property NSString* lastName;
@end

如果要訪問屬性,可以使用點語法
例如

self.firstName 點語法調用屬性
_firstName 直接調用實例變量(類內部)

屬性有很多優勢,如果使用了屬性的話,那麼編譯器就會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”。除了生成方法代碼外,編譯器還要自動向類中添加適當類型的實例變量,並且在屬性名前加下劃線(_XX),以此作爲實例變量的名字。也可以在實現文件裏通過@synthesize語法來制定實例變量:

@implementation EOCPerson
@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@end

如果阻止編譯器自動合成存取方法,就是使用@dynamic關鍵字。

@implementation EOCPerson
@dynamic firstName, lastName;
@end

屬性特質(attribute)

注意:若是自己定義存取方法,那麼就應該遵從與屬性特質相符的語義

  • 原子性

原子性:atomic(編譯器所生成的方法會通過鎖定機制確保其原子性)
非原子性:nonatomic

開發iOS程序,所有的屬性都聲明爲nonatomic,原因:iOS使用同步鎖的開銷比較大,這會帶來性能問題,一般情況下並不要屬性必須是“原子的”,因爲這並不能保證“線程安全”,如果實現“線程安全”的操作,還需採用更爲深層的鎖定機制纔可以。

  • 讀/寫權限

readwrite:獲取方法 與 設置方法
readonly:獲取方法

  • 內存管理語義

assign:針對“純量類型(scalar type)”(例如NSInteger,BOOL)的簡單複製操作。

strong:針對“擁有關係(owning relationship),爲這種屬性設置新值時,設置方法會先保留新值,並釋放舊值,然後將新值複製上去。

weak:針對“非擁有關係”(nonowning relationship),爲這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值,不同於assign的是當屬性所指的對象遭到摧毀時,屬性值也會被清空(nil)。

unsafe_unretained:語義同assign,但是它適用於對象類型,針對“非擁有關係(unretained)”,當目標遭到摧毀時,屬性值不會自動清空。

copy:設置方法並不保留新值,而是將其“copy”。多用於NSString類型。

  • 方法名

getter=:指定 獲取方法 的方法名。
setter=:制定 設置方法 的方法名。

如果要再設置屬性所對應的實例變量時,一定要遵循該屬性所聲明的語義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章