用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=:制定 設置方法 的方法名。
如果要再設置屬性所對應的實例變量時,一定要遵循該屬性所聲明的語義