第六條:理解“屬性”概念

屬性( property)是 Objecive-C的一項特性,用于封裝對象中的數(shù)據(jù)嵌戈。 Objective-C對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量抢呆。
實例變量一般通過“存取方法”(acssmethod)來訪問漓糙。其中,“獲取方法”( getter)用于讀取變量值,而“設(shè)置方法”(setter)用于寫入變量值。

例如:
在描述個人信息的類中,也許會存放人名棒仍、生日捉兴、地址等內(nèi)容÷砥В可以在類接口的 public區(qū)段中聲明一些實例變量:

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

這種寫法的問題是:對象布局在編譯期就已經(jīng)固定了庄拇。只要碰到訪問 _firsTname變量的代碼,編譯器就把其替換為“偏移量”( offset),這個偏移量是“硬編碼”( hardcode),表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)。

@interface EOCPerson : NSObject {
  @public
    NSDate *_dateOfBirth;
    NSString *_firstName;
    NSString *_lastName;
  @private
   NSString *_someInternalData;
}
@end
image.png

解決辦法:
應(yīng)用程序二進(jìn)制接口
Objective-C引入了“應(yīng)用程序二進(jìn)制接口”(Application Binary Interface韭邓,ABI)措近。
這種做法是,把實例變量當(dāng)作一種存儲偏移量所用的“特殊變量”女淑,交由“類對象”保管瞭郑。偏移量會在運行期查找,如果類的定義變了鸭你,那么存儲的偏移量也就變了屈张,這樣的話擒权,無論何時訪問實例變量,總能使用正確的偏移量阁谆,甚至可以在運行期向類中新增實例變量碳抄。

總結(jié):可以在“class-continuation分類”或?qū)崿F(xiàn)文件中定義實例變量。所以场绿,不一定要在接口把全部實例變量都聲明好剖效,可以將某些變量從接口的public區(qū)段里移走,以便保護(hù)與類實現(xiàn)有關(guān)的內(nèi)部信息焰盗。

另外一種解決辦法
盡量不要直接訪問實例變量璧尸,而應(yīng)該通過存取方法來做。但是姨谷,存取方法有著嚴(yán)格的命名規(guī)范逗宁,所以,Objective-C引入了屬性@property語法梦湘,讓編譯器自動編寫存取方法瞎颗。當(dāng)然,開發(fā)者也可以自己編寫存取方法捌议。

屬性@property

屬性(property)哼拔,能夠訪問封裝在對象里的數(shù)據(jù)。開發(fā)者可以把屬性當(dāng)做一種簡稱瓣颅,其意思是說:編譯器會自動編寫一套存取方法倦逐,用以訪問給定類型中具有給定名稱的實例變量。

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

//等同于

@interface EOCPerson : NSObject 
- (NSString*) firstName;
- (void)setFirstName : (NSString*)firstName;
- (NSString*)lastName;
- (void)setLastName : (NSString*)lastName;
@end
  • 訪問屬性宫补,可以使用“點語法”檬姥,編譯器會把“點語法”轉(zhuǎn)換為對存取方法的調(diào)用,使用“點語法”的效果與直接調(diào)用存取方法相同粉怕。
  • 如果使用了屬性的話健民,那么編譯器就會自動編寫訪問這些屬性所需的存取方法,還會自動向類添加適當(dāng)類型的實例變量(在屬性名稱前面加“_”用作實例變量的名稱)贫贝,此過程叫做“自動合成”(autosynthesis)秉犹。

@synthesis語法
通過@synthesis語法可以直接指定實例變量的名字,但一般情況下無須修改默認(rèn)的實例變量名稚晚。

@synthesis firstName = _myFirstName;

@dynamic語法
使用@dynamic關(guān)鍵字崇堵,它會告訴編譯器:不要自動創(chuàng)建實現(xiàn)屬性所用的實例變量,也不要為其創(chuàng)建存取方法客燕。而且鸳劳,在編譯訪問屬性的代碼時,即時編譯器發(fā)現(xiàn)沒有定義存取方法幸逆,也不會報錯棍辕,它相信這些方法能在運行期找到暮现。

@dynamic firstName, lastName;
屬性特質(zhì)

原子性
在默認(rèn)情況下,由編譯器所合成的方法會通過鎖定機制確保其原子性( atomicity)。如果屬性具備 nonatomic特質(zhì),則不使用同步鎖楚昭。請注意,盡管沒有名為“ atomic”的特質(zhì)(如果某屬性不具備 nonatomic特質(zhì),那它就是“原子的”( atomic)),但是仍然可以在屬性特質(zhì)中寫明這一點,編譯器不會報錯栖袋。若是自己定義存取方法,那么就應(yīng)該遵從與屬性特質(zhì)相符的原子性。

讀寫權(quán)限

  • readwrite(讀寫)特質(zhì)的屬性擁有“獲取方法”(getter)與“設(shè)置方法”抚太。若該屬性由@ synthesize實現(xiàn),則編譯器會自動生成這兩個方法方法塘幅。
  • readonly(只讀)特質(zhì)的屬性僅擁有獲取方法,只有當(dāng)該屬性由@ synthesize實現(xiàn)時,編譯器才會為其合成獲取方法。你可以用此特質(zhì)把某個屬性對外公開為只讀屬性,然后在“ class-continuation分類”中將其重新定義為讀寫屬性尿贫。

內(nèi)存管理語義
assign:設(shè)置方法”只會執(zhí)行針對“純量類型”( scalar type,例如 CGFloat或NSInteger等)的簡單賦值操作电媳。
strong:此特質(zhì)表明該屬性定義了一種“擁有關(guān)系”( owning relationship)。為這種屬性設(shè)置新值時,設(shè)置方法會先保留新值,并釋放舊值,然后再將新值設(shè)置上去庆亡。
weak:此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”( nonowning relationship)匾乓。為這種屬性設(shè)置新值時,設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)同 assign類似,然而在屬性所指的對象遭到摧毀時,屬性值也會清空( nil out)又谋。
unsafe_unretained:此特質(zhì)的語義和 assign相同,但是它適用于“對象類型"”( object type),該特質(zhì)表達(dá)一種“非擁有關(guān)系”(“不保留”, unretained),當(dāng)目標(biāo)對象遭到摧毀時,屬性值不會自動清空(“不安全”, unsafe,這一點與weak有區(qū)別拼缝。
copy:此特質(zhì)所表達(dá)的所屬關(guān)系與 strong類似。然而設(shè)置方法并不保留新值,而是將其“拷貝”(copy)彰亥。當(dāng)屬性類型為 NSString*時,經(jīng)常用此特質(zhì)來保護(hù)其封裝性,因為傳遞給設(shè)置方法的新值有可能指向一個 NSMutableString類的實例咧七。這個類是NSString的子類,表示一種可以修改其值的字符串,此時若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會在對象不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變”( immutable)的字符串,確保對象中的字符串值不會無意間變動任斋。只要實現(xiàn)屬性所用的對象是“可變的”( mutable),就應(yīng)該在設(shè)置新屬性值時拷貝一份继阻。

方法名
  • getter=<name> :指定“獲取方法”的方法名。如果某屬性是Boolean型废酷,可以通過這個辦法為其獲取方法加上“is”前綴瘟檩。
  • setter=<name> :指定“設(shè)置方法”的方法名。這種用法不常見澈蟆。

通過上述特質(zhì)芒帕,可以微調(diào)由編譯器所合成的存取方法。不過需要注意:若是自己來實現(xiàn)這些存取方法丰介,那么應(yīng)該保證其具備相關(guān)屬性所聲明的特質(zhì)。
例如鉴分,如果將某個屬性聲明為copy哮幢,那么就應(yīng)該在“設(shè)置方法”中拷貝相關(guān)對象,否則會誤導(dǎo)該屬性的使用者志珍,而且橙垢,若是不遵從這一約定,還會令程序產(chǎn)生bug伦糯。

/* 頭文件  */
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;

- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;

/* 實現(xiàn)文件  */

- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName{
    if (self = [super init]){
        // firstName有可能是NSMutableString*柜某,該字符串的值可能會在對象不知情的情況下遭人修改嗽元。所以,要拷貝一份“不可變”的字符串喂击,確保對象中的字符串值不會無意間變動剂癌。
        _firstName = [firstName copy]; 
        _lastName = [lastName copy];
    }
    return self;
}

盡量使用不可變的對象,也就是說翰绊,屬性應(yīng)該設(shè)為“只讀”佩谷。用初始化方法設(shè)置好屬性值之后,就不能再改變了监嗜。

@property (copy, readonly) NSString *firstName;
@property (copy, readonly) NSString *lastName;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谐檀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子裁奇,更是在濱河造成了極大的恐慌桐猬,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刽肠,死亡現(xiàn)場離奇詭異溃肪,居然都是意外死亡,警方通過查閱死者的電腦和手機五垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門乍惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人放仗,你說我怎么就攤上這事润绎。” “怎么了诞挨?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵莉撇,是天一觀的道長。 經(jīng)常有香客問我惶傻,道長棍郎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任银室,我火速辦了婚禮涂佃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜈敢。我一直安慰自己辜荠,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布抓狭。 她就那樣靜靜地躺著伯病,像睡著了一般。 火紅的嫁衣襯著肌膚如雪否过。 梳的紋絲不亂的頭發(fā)上午笛,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天惭蟋,我揣著相機與錄音,去河邊找鬼药磺。 笑死告组,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的与涡。 我是一名探鬼主播惹谐,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼驼卖!你這毒婦竟也來了氨肌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酌畜,失蹤者是張志新(化名)和其女友劉穎怎囚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桥胞,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡恳守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贩虾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片催烘。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖缎罢,靈堂內(nèi)的尸體忽然破棺而出伊群,到底是詐尸還是另有隱情,我是刑警寧澤策精,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布舰始,位于F島的核電站,受9級特大地震影響咽袜,放射性物質(zhì)發(fā)生泄漏丸卷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一询刹、第九天 我趴在偏房一處隱蔽的房頂上張望谜嫉。 院中可真熱鬧,春花似錦凹联、人聲如沸骄恶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虐呻,卻和暖如春象泵,著一層夾襖步出監(jiān)牢的瞬間寞秃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工偶惠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留春寿,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓忽孽,卻偏偏與公主長得像绑改,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兄一,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355