iOS -- 理解 屬性 的概念 (5)

?理解 屬性 的概念

屬性會自動生成存取方法, ?可以利用點語法調用, ?

若不想編譯器自動合成存取方法, 可以自己實現, 還有另外一種方法, 就是使用 @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];

}

}


總結:

可以通過 '特質' 來指定存儲數據所需的正確語義

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容