0x01 存取方法是間接的一個例子
上一篇中,我們已經構造了一臺“車”。
假設現在我想要隨時更換自己喜歡的發動機和輪胎,就要用到“存取方法”。
“存取方法”是用來讀取或改變某個對象屬性的方法,例如此前見過的setFillColor:就是一個存取方法。
如果我們要對其他對象中的屬性進行操作,應該儘量使用對象提供的存取方法,絕對不要直接改變對象裏面的值,而應該使用setter方法進行更改。
正確使用“存取方法”將會使程序更加靈活。
0x02 方法的命名規則
1、雖然說方法的命名是自由的,但爲了程序的可讀性,我們還是應該遵循Cocoa的慣例:
setter方法應根據它所要更改的屬性名稱來命名,如“set+屬性名”;
getter方法只需以其返回的屬性名命名即可,即“屬性名”。
因爲get這個詞在Cocoa中有特殊含義,如果get出現在Cocoa的方法名稱中,就意味着這個方法會將傳遞給它的參數作爲指針來返回數值。
2、setter方法和getter方法一般是成對出現的,當然也可以不成對出現。如對於只讀特性只有getter方法,對於密碼特性只有setter方法。
0x03 存取方法的實現
我們先在Car的接口中對添加的setter和getter方法進行聲明:
@interface Car : NSObject
{
Engine *engine;
Tire *tires[4];
}
//New Code
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
- (Tire *) tireAtIndex: (int) index;
- (void) setTire: (Tire *) tire atIndex: (int) index;
//New Code
- (void) print;
@end // Car
第一對存取方法是爲了訪問發動機的屬性:
- (Engine *) engine;
- (void) setEngine: (Engine *) newEngine;
調用Car對象的engine方法可以訪問engine變量,調用setEngine:方法可以更改發動機屬性。
下面是這兩個方法的實現:
- (Engine *) engine
{
return (engine);
} // engine
- (void) setEngine: (Engine *) newEngine
{
engine = newEngine;
} // setEngine
我們看到getter方法engine返回的是一個指針,指向Car中的Engine對象。實際上是返回實例變量engine的當前值;
同樣的,setter方法setEngine:將newEngine的值賦給engine實例變量。實際上被修改的是engine的指針值。
在Objective-C中所有對象間的交互都是通過指針實現的!
第二對存取方法是爲了訪問輪胎的屬性:
- (void) setTire: (Tire *) tire atIndex: (int) index;
- (Tire *) tireAtIndex: (int) index;
由於汽車的4個輪胎處於4個位置,所以Car對象中必須包含一個表示輪胎位置的數組:
- (void) setTire: (Tire *) tire atIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in setTire:atIndex:",index);
exit (1);
}
tires[index] = tire;
} // setTire:atIndex:
- (Tire *) tireAtIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in "tireAtIndex:", index);
exit (1);
}
return (tires[index]);
} // tireAtIndex:
因爲tire是一個C風格的數組,對於其索引值是否有效,編譯器是可能直接忽略的。所以我們自己得用if條件語句來判斷index值是否合理,這是一種防禦式編程思想。
setTire:方法的聲明格式看起來不太一樣,這是因爲它擁有兩個參數,往後學習會更加理解這種寫法。
最後修改main()函數:
int main (int argc, const char * argv[]) {
Car *car = [Car new];
//事實上可以在Car類中把init方法刪掉,因爲下面的代碼已經做了init方法的工作
Engine *engine = [Engine new];
[car setEngine: engine];
for (int i = 0; i < 4; i++) {
Tire *tire = [Tire new];
[car setTire: tire atIndex: i];
}
[car print];
return (0);
} // main
程序運行結果沒有變化,但我們改進了它的內部結構,使程序更加靈活:
I am an engine. Vrooom!
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.
I am a tire. I last a while.
0x04 存取方法的應用
我們已經爲Car類添加了存取方法,那換配置就是隨心所欲的事了。
現在來爲這臺車換一個發動機:
@interface Slant6 : Engine
@end // Slant6
@implementation Slant6
- (NSString *) description
{
return (@"I am a slant- 6. VROOOM!");
} // description
@end // Slant6
創建這臺Slant6發動機,繼承了Engine超類的屬性。但是重寫了description方法,輸出了新發動機的信息。
接着我們換輪胎:
@interface AllWeatherRadial : Tire
@end // AllWeatherRadial
@implementation AllWeatherRadial
- (NSString *) description
{
return (@"I am a tire for rain or shine.");
} // description
@end // AllWeatherRadial
最後修改main()函數,這臺車使用了全新的發動機和輪胎:
int main (int argc, const char * argv[]) {
Car *car = [Car new];
for (int i = 0; i < 4; i++)
{
//New Code
Tire *tire = [AllWeatherRadial new];
//New Code
[car setTire: tire atIndex: i];
}
//New Code
Engine *engine = [Slant6 new];
//New Code
[car setEngine: engine];
[car print];
return (0);
} // main
運行程序後,可以發現輸出已經更新爲新配置了:
I am a slant- 6. VROOOM!
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.
I am a tire for rain or shine.