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