?理解 屬性 的概念
屬性會自動生成存取方法, ?可以利用點語法調用, ?
若不想編譯器自動合成存取方法, 可以自己實現, 還有另外一種方法, 就是使用 @dynamic 關鍵字, 它會告訴編譯器, 不要自動創建實現屬性所用的實例變量 和 存取方法, ? 而且,在編譯訪問屬性的代碼時, 即使編譯器發現沒有定義存取方法, ?也不會報錯, 它相信這些方法會在運行期找到, 使用方法是, 在 頭文件中(.h 文件) 定義屬性, 在 實現文件 (.m 文件) 使用 @dynamic 屬性名;
定義了屬性之后, 可以在類的 實現代碼 (即.m 文件)中通過 @synthesize 語法來指定實例變量的名字, 而不再使用默認的名字, 但是一般不推薦使用此方法, 因為不方便閱讀
// 屬性特質
屬性可以擁有的特質分為四類:
原子性?
定義: 在 并發編程中. 如果其操作具備 整體性, 也就是說, 系統 其它部分 無法觀察 到 其中間步驟所生成的 臨時結果, 而只能看到 操作前 和 操作后 的結果, 那么該操作就是 '原子的'(atomic), 或者說, 該操作具備 '原子性'(atomicty)
在默認情況下, 有編譯器所合成的方法會通過鎖定機制確保其原子性 (atomicity), 如果屬性具備nonatomic 特質, 則不使用同步鎖, 請注意, 盡管沒有名為 'atomicity' 的特質, 但是如果屬性不具備 nonatomic ?特質, 那它就是 '原子的', ?也可以在屬性的特質中寫明這一點, 編譯器不會報錯
atomic 與 nonatomic 的區別
具備 atomic 特質的獲取方法會通過鎖定機制來確保其操作的原子性.? 也就是說, 如果兩個線程同時讀取一個屬性,那么無論何時, 總能得到有效的屬性值 ?(當線程A進行寫操作,這時其他線程的讀或者寫操作會因為等該操作而等待。當A線程的寫操作結束后,B線程進行寫操作,所有這些不同線程上的操作都將依次順序執行——也就是說,如果一個線程正在執行 getter/setter,其他線程就得等待。)
使用 nonatomic , 如果其中一個線程正在改變其屬性值的時候, 另外一個線程也許會突然闖入, 把尚未修改好的屬性值讀取出來, 這種情況下, 線程讀取到的屬性值 肯定 不一致
一般 iOS 程序中, 所有屬性都聲明為 nonatomic , 這樣是因為在 iOS 中使用同步鎖的開銷比較大, 會帶來性能問題, 一般情況下也不會要求屬性必須是原子的, 而且即使設置了 atomic 也不能保證絕對的線程安全
讀/寫 權限
具備 readwrite (可讀可寫)特質的屬性擁有 獲取方法 (getter) 與 設置方法(setter),?
具備 readonly (只讀) 特質的屬性僅擁有 獲取方法
內存管理語義
下面這一組特質僅會影響 '設置方法', 編譯器在合成存取代碼時, 要證據此特質來決定所生成的代碼, 如果自己編寫存取方法, 那么就必須同有關屬性所具備的特質相符 .
assign 設置方法 只會執行 純量類型 (如 CGFloat 或 NSInteger) 的簡單賦值操作
strong 此特質表明該屬性定義了一種 '擁有關系', 為這種屬性設置新值時, 設置方法會先保留新值, 并釋放舊值, 然后在將新值設置上去
weak 此特質表明了一種 '非擁有關系', 為這種屬性設置新值時, 設置方法既不保留新值, 也不釋放舊值, 然而在屬性所指的對象遭到摧毀時, 屬性值也會清空
copy 此特質所表達的所述關系與 strong 類似, ?然而設置方法并不保留新值, 而是將其拷貝,( 當屬性類型為 NSString * 時, 經常用此特質來保護其封裝性, ?因為傳遞給設置方法的新值有可能指向一個 NSMubleString 類的實例, 這個類是 NSString 的子類, 表示一種可以修改其值的字符串, 此時若是不拷貝字符串,那么設置完屬性之后, 字符串的值就可能會在對象不知情的情況下造人更改), 只要實現屬性所用的對象是 '可變的', 就應該在設置新屬性值時拷貝一份.
方法名
可通過如下特質來指定存取方法的方法名
getter=<name> 指定 '獲取方法' 的方法名, 如果某屬性是 Boolean 型, 而你想為其獲取方法加上 'is' 前綴. 那么就可以用這個方法來指定
例如: @property (nonatomic, getter-isOn) BOOL on;
setter=<name> ?指定 '設置方法'的方法名
通過上述特質, 可以微調由編譯器所合成的存取方法, 不過需要注意的是, 若是自己來實現這些存取方法, 那么應該保證其具備相關屬性所聲明的特質.
如果想在其他方法里設置屬性值, 那么同樣要遵循屬性定義中所宣稱的語義, 例如:一個類新增了一個初始化方法
@interface EOCPerson : NSManagedObject
@property (copy) NSString * firstName;
@property (copy) NSString * lastName;
- (id)initWithFirstName:(NSString*)firstName lastName:(NSString *)lastName;
@end
在實現這個自定義的初始化方法時, 一定要遵循屬性定義中宣城的 'copy' 語義, 因為 '屬性定義' 就相當于 '類' ?和 '待設置的屬性值' ?之間所達成的 契約
初始化的方法可以這么寫:
- (id)initWithFirstName:(NSString*)firstName lastName:(NSString *)lastName{
if (self == [super init]){
_firstName = [firstName ?copy];
_lastName = [lastName ?copy];
}
}
總結:
可以通過 '特質' 來指定存儲數據所需的正確語義
在設置屬性所對應的實例變量時, 一定要遵循從該屬性所聲明的語義