Apple在Objective-C 2.0中引入了屬性(Property),它組合了新的預編譯指令和新的屬性訪問器語法。
新的屬性功能可以顯著減少必須編寫的代碼行數,讓代碼看起來更幹練,也可減少出現 Bug的機會。
0x01 使用屬性值
使用property屬性,編譯器會自動生成屬性的setter / getter方法,還會自動生成對應加下劃線的實例變量名。
由於自定義的變量名跟獲取函數名一樣,爲了區分,實際的變量名在前面加下劃線。
雖然默認是加下劃線,但可以在實現文件中使用關鍵詞@synthesize自定義實際的變量名。
加下劃線的變量名由於是編譯過程中生成的,所以我們看不到,但事實存在。
所以,我們的AllWeatherRadial類接口代碼可以大大簡化:
//Original
#import <Foundation/Foundation.h>
#import "Tire.h"
@interface AllWeatherRadial : Tire {
float rainHandling;
float snowHandling;
}
//手動編寫的setter/getter方法
- (void) setRainHandling: (float) rainHanding;
- (float) rainHandling;
- (void) setSnowHandling: (float) snowHandling;
- (float) snowHandling;
@end
//---------------------------------------------------------------------------------------
//Using @property
#import <Foundation/Foundation.h>
#import "Tire.h"
@interface AllWeatherRadial : Tire
//使用屬性風格聲明rainHandling和snowHandling的屬性,並刪除之前的setter/getter方法
@property float rainHandling;
@property float snowHandling;
@end
0x02 實際存在的下劃線變量名
@property 在Xcode 4.5版本後得到了增強,一行代碼就可以完成setter / getter方法的聲明和實現。
但是當我們重寫了setter / getter方法或者在實現其他方法的過程中使用到之前未使用屬性風格聲明的變量就會報錯。
比如指定初始化方法的報錯,找不到變量:
原因在於,rainHandling和snowHandling已被聲明爲屬性,現在的成員變量名是_rainHandling和_snowHandling。
我們操作的目標是成員變量。
很簡單,把報錯的屬性名前面加上下劃線變成成員變量名即可(證明下劃線變量名事實存在):
@implementation AllWeatherRadial
- (id) initWithPressure:(float)p
treadDepth:(float)td
{
if (self = [super initWithPressure: p
treadDepth: td]) {
_rainHandling = 24.7;
_snowHandling = 42.5; //添加屬性後,必須使用下劃線變量名
}
return (self);
} // initWithPressure:treadDepth
0x03 點表達式仍然適用
在AllWeatherRadial.h中有個description方法,使用屬性後,點表達式仍然不需要做修改。
通過點語法訪問變量,等效於調用自動生成的存取方法訪問變量:
- 如果點表達式出現在等號左邊,調用的是變量的setter方法;
- 如果點表達式出現在等號右邊,調用的是變量的getter方法。
- (NSString *) description
{
NSString *desc;
desc = [[NSString alloc] initWithFormat:
@"AllWeatherRadial: %.1f / %.1f / %.1f / %.1f",
self.pressure, self.treadDepth, self.rainHandling, self.snowHandling];
//點表達式不需要修改
return (desc);
} // description
@end // AllWeatherRadial
0x04 自定義變量名
如果我們覺得_rainHandling和_snowHandling名字太長了,多個下劃線用起來也不順手,可以自定義變量名。
@synthesize 編譯器指令可以達到該目的:
#import "AllWeatherRadial.h"
@implementation AllWeatherRadial
//自定義變量名
@synthesize rainHandling = rain;
@synthesize snowHandling = snow;
- (id) initWithPressure:(float)p
treadDepth:(float)td
{
if (self = [super initWithPressure: p
treadDepth: td]) {
rain = 24.7;
snow = 42.5; //這裏就能用自定義變量名了
}
return (self);
} // initWithPressure:treadDepth
在Xcode 4.5版本之前,@synthesize的和@property成對出現來自動合成指定屬性變量的存取方法的。
但實際上爲了減少不必要的麻煩,用@synthesize自定義實例變量名的行爲是不被建議的。
因爲有很多程序員習慣上會調用編譯器生成的下劃線變量名,如果自定義了名稱,依賴於該頭文件的代碼均會報錯。
但使用self指針的點方法仍然有效,因爲self始終指向調用此方法的當前對象。
0x05 禁止存取方法自動合成
@dynamic編譯器指令相當於告訴編譯器:“參數的getter和setter方法並不在此處,而在其他地方實現了或者生成了,當你程序運行的時候你就知道了,所以別警告我了。”
這樣程序在運行的時候,對應參數的setter / setter方法就會在其他地方去尋找,比如超類裏。
但如果聲明瞭dynamic屬性,卻實際上沒有對應的setter / setter方法,編譯時可能沒有異常,程序實際運行中碰到需要調用setter / setter方法的地方將崩潰,因爲調用了不存在的方法。
@interface father()
@property (nonatomic, strong, readwrite) NSObject *sampleObject;
...
@synthesize sampleObject = _sampleObject;
@end
//------------------------------------------------------------------------
@interface child : father
@property (nonatomic, strong, readwrite) NSObject *sampleObject;
...
@dynamic sampleObject;
@end