教程詳細:
技術:Objective-C 難度:初學者 完成時間:20-35分鐘
歡迎來到學習Objective-C系列教程的第四部分,到目前為止,我們在理論,原則以及語言功能。今天,我們會建立一個像前面舉的汽車例子那樣的簡單類。我們的類會較長的描述汽車,并允許我們讀寫其屬性。在今天的例子之後,你将能夠用Xcode建立自己的類并熟練運用它。
到目前為止,我們已經收到了一些很好的回報,包括通過郵件,留言,twitter等。我很高興看到這麼多童鞋對這系列教程感興趣,更高興的是,看到大家都動起手來實踐并提出問題。呵呵,繼續努力吧!
準備開始
首先用Xcode建立一個新項目,在Mac OS X 的分隔符下,點選Application,然後點選Command Line Tool,最後,更改下清單框設定Foundation的類型。
儲存該項目,我就命名為CarApp吧,當這項目視窗出現時,我們需要建立一個新類。點選Command-N(或 File>New File),在Mac OS X下導航到Cocoa并選擇Objective-C類。確定子類是繼承于NSObject并點選下一步。命名你的類為SimpleCar并確定 .h 檔案被建立了,然後就儲存。
我們的類現在已經建立好了,但它是空的。讓我們為它填充些代碼吧。還記得在Objective-C中,我們把代碼分成兩部分嗎:接口與實作。它讓在接口的工作變得有邏輯意義,是以這是我們的初衷。
編寫接口
打開SimpleCar.h檔案,目前的狀态看起來是這樣的:
2.
3. @interface SimpleCar : NSObject {
4.
5. }
6.
7. @end
首先,我們引入Cocoa.h,這使我們可以獲得像NSString,NSMutableString等這樣的東東。然後,我們建立類(SimpleCar)作為NSObject的子類。
現在, 我們需要确定我們的類需要儲存什麼樣的資訊了。既然我們像平常一樣用汽車,我們需要存儲有關汽車的相關資訊,例如:
- make 牌子
- model 型号
- VIN 汽車識别碼
還有更多資訊我們可以寫進,但現在不會這樣。對于其中的每個屬性,我們需要用一個合适的資料類型去存儲。牌子和型号是字元範疇(像檔案,号碼和标點符号),是以用string是比較合适的喲。VIN(汽車識别碼)僅僅是由數字組成。我們的代碼看起來會像這樣(省略前面):
1. @interface SimpleCar : NSObject {
2. NSString* make;
3. NSString* model;
4. NSNumber* vin;
5. }
6.
7. @end
我們之前也提過了,為了讀寫類的資料,會用到一個方法。是以,要設定變量,我們需要增加方法。要做這樣,我們提出四點:一個設定牌子,一個設定型号,一個設定VIN,以及最後一個方法用來設定牌子和型号(隻是為了告訴大家怎樣使用多個參數罷了)。
1. @interface SimpleCar : NSObject {
2. NSString* make;
3. NSString* model;
4. NSNumber* vin;
5. }
6.
7. // set methods
8. - (void) setVin: (NSNumber*)newVin;
9. - (void) setMake: (NSString*)newMake;
10. - (void) setModel: (NSString*)setModel;
11.
12. // convenience method
13. - (void) setMake: (NSString*)newMake
14. andModel: (NSString*)newModel;
15.
16. @end
我們在大括号@end之間聲明方法,在方法之前放置 “-“是要告訴編譯器這個方法是執行個體方法。一個執行個體方法是依靠執行個體執行的方法。相反,一個”+”表示這個方法是類方法,這方法的執行并不需要一個單獨的對象,這接下來會講到。
我們的第一個方法(setVin)傳回void,并以一個NSNumber作為參數。我們的第二個方法(setMake)也相類似,也傳回void,并以一個NSString作為參數。第三個方法除了方法名都相似。
我們最後一個方法也是傳回void,但需要兩個NSString類型的參數:newMake和newModel。這些方法的命名是與大多數Objective-C方法相似的,都是以簡單英語命名。是以,當你讀到這方法時,能夠明顯知道其含義。記住方法的名記是重要的,在這種情況下為:”setMake,andModel“--所有參數名都包括在方法名裡了。
有一點值得注意的是,我們使用(void)是因為我們不需要傳回任何東西。既然所有要做的隻是設定值,并不需要傳回任何東西(例如一表示成功的資訊):
下一步,我們會添加用來通路值的方法。雖然我們稱這些方法為get 方法和set方法,我們隻用”set”和”get”。怎樣命名你的方法由你決定,但是用到了”get”會常于的,有助于避免混淆。
我們的set方法看起來會像這樣:
1. // set methods
2. - (void) setVin: (NSNumber*)newVin;
3. - (void) setMake: (NSString*)newMake;
4. - (void) setModel: (NSString*)newModel;
5.
6. // convenience method
7. - (void) setMake: (NSString*)newMake
8. andModel: (NSString*)newModel;
9.
10. // get methods
11. - (NSString*) make;
12. - (NSString*) model;
13. - (NSNumber*) vin;
要注意的是, get方法名和類裡的變量名相同。這樣當我們讀書變量時更簡單。當你直接通路這變量,基本上對get方法是透明的。
編寫實作
現在,我們的接口已經存在了,并且知道類要幹什麼了,是以要實作我們的方法了。往回看,我們有四個方法需要實作的:setVin, setMake ,setModel 和setMake:andModel。在移動檔案之前,複制這些方法的聲明到粘貼闆(Cmd+C)。現在關閉SimpleCar.h 并在編輯器裡打開 SampleCar.m ,在@implementation和@end之間粘貼下這些方法聲明。如下:
[email protected] SimpleCar
2.
3.// set methods
4.- (void) setVin: (NSNumber*)newVin;
5.- (void) setMake: (NSString*)newMake;
6.- (void) setModel: (NSString*)newModel;
7.
8.// convenience method
9.- (void) setMake: (NSString*)newMake
10. andModel: (NSString*)newModel;
11.
12.// get methods
13.- (NSString*) make;
14.- (NSString*) model;
15.- (NSNumber*) vin;
16.
顯然,這是不對的,我們需要做的是,把花括号的地方和方法的内部工作交換,像這樣:
1: @implementation SimpleCar
3: // set methods
4: - (void) setVin: (NSNumber*)newVin {
6: }
8: - (void) setMake: (NSString*)newMake {
10: }
12: - (void) setModel: (NSString*)newModel {
14: }
16: - (void) setMake: (NSString*)newMake
17: andModel: (NSString*)newModel {
19: }
21: // get methods
22: - (NSString*) make {
24: }
26: - (NSString*) model {
28: }
30: - (NSNumber*) vin {
32: }
34: @end
現在,我們需要為方法添加一些代碼了。讓我們以getter方法作為開始吧,對于每一個getter方法,我們需要做的就是確定方法傳回打算傳回的資料。由于這個原因,我們的getter方法像這樣:
1: - (NSString*) make {
2: return make;
3: }
5: - (NSString*) model {
6: return model;
7: }
9: - (NSNumber*) vin {
10: return vin;
11: }
記住:方法傳回的變量是決定于接口檔案的,不要把方法名和變量名混淆了喲!
這很簡單吧,我們調用make方法,這make方法傳回一個NSString指針--這情況下是make變量。同樣對于model和vin(除了vin傳回一個數字)。
現在說說setter方法,首先,我們看看這些代碼,然後将越過去,我們的setter方法看上去像這樣的:
1: // set methods
2: - (void) setVin: (NSNumber*)newVin {
4: [vin release];
5: vin = [[NSNumber alloc] init];
6: vin = newVin;
8: }
10: - (void) setMake: (NSString*)newMake {
12: [make release];
13: make = [[NSString alloc] initWithString:newMake];
15: }
17: - (void) setModel: (NSString*)newModel {
19: [model release];
20: model = [[NSString alloc] initWithString:newModel];
22: }
24: // convenience method
25: - (void) setMake: (NSString*)newMake
26: andModel: (NSString*)newModel {
28: // Reuse our methods from earlier
29: [self setMake:newMake];
30: [self setModel:newModel];
32: }
set方法比get方法有那麼一點點棘手。我們想把值傳遞到每個方法裡,為了它們屬于這個類。首先,公開這些變量,他們被配置設定了記憶體,如果不被配置設定,則為零,而忽略了對象傳遞給他們的消息。當我們讨論記憶體管理的時候,我們将介紹這些問題。
因為我們在setter方法為我們的對象配置設定記憶體,當這對象被從記憶體裡釋放時,我們要確定釋放它們。做到這一點,我們需要自定義一個釋放方法,像這樣的:
1: -(void) dealloc
2: {
3: [vin release];
4: [make release];
5: [model release];
6: [super dealloc];
7: }
測試類
恭喜你,如果你按照上面所說的做,你現在應該有一個可工作的類了。是以,讓我們測試它吧。
打開項目主檔案(我的是叫CarApp.m),預設情況下看起來像這樣的:
3: int main (int argc, const char * argv[]) {
5: NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
7: // Insert custom code here...
8: NSLog(@"Hello, World!");
10: [pool drain];
11: return 0;
12: }
删除評論和NSLog行,因為我們不再需要它們了。
為了開始使用我們的類,我們需要把它放時程式裡。在下面原始的#import行加上下面這行:
1: #import "SimpleCar.h"
我們的類現在是可用的了,為了測試它,但我們需要建立一個執行個體。這裡有完整的代碼:
2: #import "SimpleCar.h"
4: int main (int argc, const char * argv[]) {
6: NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
8: SimpleCar *myCar = [[SimpleCar alloc] init];
10: NSNumber *newVin = [NSNumber numberWithInt:123];
12: [myCar setVin:newVin];
13: [myCar setMake:@"Honda" andModel:@"Civic"];
15: NSLog(@"The car is: %@ %@", [myCar make], [myCar model]);
16: NSLog(@"The vin is: %@", [myCar vin]);
18: [myCar release];
20: [pool drain];
22: return 0;
23: }
首先,我們建立一個SimpleCar命叫myCar的執行個體的指針。接着,我們使用記憶體分布和初始化--這些會在下面說到的:
其次,既然我們需要傳遞一個NSNumber給setVin方法,在這就建立一個呗。再次,我們建立一個命叫newVin的NSNumber執行個體的指針,并且初始化值為123。 “123”是一個整型,這就是我們使用數字的原因了。
接下來,我們調用我們的方法,首先我推送myCar該接收的消息,并使用setVin方法。冒号後的值(之前建立的NSNumber)是我們提供給這方法的。接着,我們調用兩個參數的setMake方法。這些參數由一個@打頭,是為了告訴編譯器緊接的是一個string。
最後,我們釋放myCar,因為之前用了它。接下來的會更多的讨論記憶體管理。
我們的類在工作了,為了證明這動作,我們加一些NSLog語句來列印一些值到控制台吧。如果你打開控制台(Run>Console),建立并運作你的程式,你會看到像這樣的:
屬性和合成(Synthesize)
看看上面的代碼,很多看起來像是乏味的。例如,在我們的getter方法裡,隻做了傳回一個執行個體變量,但這就要用了三行代碼去做如此簡單的事。同樣,在我們的setter方法裡,我們僅設定了執行個體變量。除了我們方法有兩個參數,看起來是重複和臃腫呀。然而,Objective-C為了解決這問題,用了@property和@synthesize,放在我們的通路方法那裡,并給出了許多簡潔的編碼。
這樣,我們新接口檔案看起來像使用屬性那樣了:
1: #import "SimpleCar.h"
3: @implementation SimpleCar
5: @synthesize make, model, vin;
7: - (void) setMake: (NSString*)newMake
8: andModel: (NSString*)newModel {
10: [self setMake:newMake];
11: [self setModel:newModel];
13: }
15: @end
這看起來是不是更優雅呢?這樣想吧,@property替換了所有getter和setter方法的接口聲明,而@synthesize則替換它們實際的方法。這樣 , getter和setter可以被動态建立了,我們不需要浪費時間去建立它們,除非我們需要一些特别的事情。
小結:
到現在,你應該很好的掌握了類,對象和執行個體了。當然,如果你還沒有建立類,那就另當别論了,這東西是需要時間的。通過例子學習是更好的方法,如果你不想動手,那至少也要下載下傳代碼去閱讀一下,以確定百分百知道這是神馬回事。