Objective-C中的屬性(property) 它組合了新的預編譯指令和新的屬性訪問的語法,新的屬性功能顯著減少了必須編寫的冗長代碼的數量。 下面我們來比較下面的代碼 復制代碼 //第一種聲明方法 -(void)setRainHandling:(float) rainHanding; -(float) rainHandling; -(void)setSnowHandling:(float) snowHandling; -(float) snowHandling; //第二種聲明方法 @property float rainHandling; @property float snowHandling; 復制代碼 以上兩段代碼的作用是完全一樣的,由此我們可以得出屬性的作用是: @property預編譯指令的作用是自動聲明屬性的setter和getter方法,事實上,屬性的名稱不必與實例變量的名稱相同,但大多數情況下它們是一樣的。 我們知道了函數的settter和getter方法的聲明,但是如何實現呢?我們接著看 復制代碼 //第一種實現方法 -(void)setRainHandling:(float) rh { rainHandling =rh; } -(float) rainHandling { return rainHandling; } -(void)setSnowHandling:(float) sh { snowHandling =sh; } -(float) snowHandling { return snowHandling; } //第二種實現方法 @synthesize rainHandling; @synthesize snowHandling; 復制代碼 我們的目的是徹底的刪除setter和getter方法,用以上的兩行代碼就可以完全代替。 對於synthesize這種編譯器的功能有以下幾點需要說明: @sythesize它表示“創建了該屬性的訪問代碼”。當遇到@sythesize rainHandling;這行代碼時,編譯器將添加實現 -setRainHandling:和-rainHandling方法的預編譯代碼。 Cocoa的訪問器編寫實用工具和其他平台上的UI生成器可以生成源代碼,這些源代碼隨後會被編譯。但是@sythesize預編譯指令不同於代碼生成。你永遠也不會看到實現-setRainHandling:和-ranHandling的代碼,這些方法確實存在並可以調用。 在X-code 4.5以後的版本中,可以不必使用@sythesize了。 所有的屬性都是基於變量的,所以在你合成setter和getter方法的時候,編譯器會自動創建與屬性名稱相同的實例變量。如果你沒有聲明這些變量,編譯器也會聲明的。如果我們不去自己實現@sythesize,編譯器會給我們生成一個下滑線(_)開頭的實例變量。如在頭文件的定義中寫@property (copy) NSString name; 在其實現中會有一個_name的實例變量。 點表達式的妙用 Objective-C 2.0的屬性引用了一些新的語法特性,使我們更加容易訪問對象的屬性,也就是點表達式。 如果點表達式出現了等號(=)的左邊,該變量名稱的setter方法將被調用。如果點表達式出現在了對象變量的右邊,則該變量的getter方法將被調用。 點表達式只是調用訪問方法的一種便捷方式,並沒有什麼神秘之處。 屬性的擴展 assign //簡單賦值,主要用於基本數據類型 copy //創建一個新的對象,新的對象和舊對象是獨立的兩個對象 retain //將對象計數器加1 readonly //表示只讀屬性 只會生成getter方法 不會生成setter方法 readwrite //默認值,表求生成setter和getter方法 nonatomic //非原子訪問,不加同步 ,多線程並發訪問提高性能 (對多線程的保護,防止在未寫完,被另一個線程讀取,造成數據錯誤) 名稱的使用 一種非常普遍的情況是屬性的名稱與支持屬性的實例變量名稱相同,不過,有時候你可能希望實例變量是一個名稱,而公開的屬性是另一個名稱。 復制代碼 //頭文件的定義 #import "Tire.h" //關於適應所有天氣的Tire類的聲明與定義 @interface AllWeatherTire : Tire { NSString *tireName; } @property float rainHanding; @property float snowHanding; //用於給我們我的輪胎起一個名字 @property (copy)NSString *name; //實現 #import "AllWeatherTire.h" @implementation AllWeatherTire @synthesize name = tireName; - (NSString*)description { NSString *desc; desc = [NSString stringWithFormat:@"AllWeatherTier:name is %@,%.1f,%.1f, %.1f, %.1f", self.name,self.pressure,self.treadDepth,self.rainHanding,self.snowHanding]; return desc; } 復制代碼 在這裡編譯器仍創建-setName:和-name方法,但在其實現代碼中用的卻是tireName實例變量。不過這樣做的話,編譯的時候將會遇到一些錯誤。因為我們直接訪問的實例變量已經被修改了。我們可以選擇用搜索並替換的name的方式來解決,也可以將實例變量的直接調用改成用訪問的方法。比如在init方法中把 name =@“Car” 改成self.name =@"Car". 自己動手有時更好 我們之前提到過屬性是基於變量的,並且編譯器會為你創建setter和getter方法。但是如果你不想要變量、setter和getter方法的話應該怎麼辦? 答案是使用@dynamic關鍵字。 如果你聲明了dynamic屬性,並且企圖調用不存在的getter和setter方法,你將會得到一個錯誤。 復制代碼 @property(readonly) float bodyMassIndex; @dynamic bodyMassIndex; -(float)bodyMassIndex { /// ; } //如果調用不存在的setter和getter方法會報錯。 復制代碼 屬性的缺點 屬性不是萬能的,如果方法並不適合屬性所胡涵蓋的較小范圍的話。屬性只支持替代 -setBlah和-blah方法,但是不支持那些需要接收額外參數的方法。例如:car對象中tire對象的代碼。 -(void)setTire:(Tire *)tire atIndex:(int)index; -(Tire *)tireAtIndex:(int)index; 屬性的總結 本文主要介紹了屬性。在為對象變量執行常見的操作時,利用屬性可以減少需要編寫以及隨後需要閱讀的代碼數量。使用@propert預編譯指令可以告訴編譯器:“嘿,這個對象具有這些類型的特性” 你還可以讓屬性傳遞其他信息,比如可變性(只讀或者讀寫)。編譯器在後台會自動生成對象變量的setter和getter方法。 使用@sythesize預編譯指令可以通知編譯器生成訪問的方法。你還可以控制由編譯器生成的訪問方法對哪些實例變量起作用。如果不想使用默認的行為,你完全可以編寫自己的訪問方法。你還可以使用@dynamic指令告訴編譯器不要生成變量和代碼。 盡管點表達式通常出現在有屬性的代碼中,但是它只是調用對象的setter和getter方法的一種便捷方式。點表達式法減少了需要鍵入的字符數量,而且進一步方便了曾經使用其他語言的編程人員。