Objective C 教程 @Part-02

● @property & @synthesize

在Objective C宣告的變數(variable)其對外的存取權限預設都是Protected,在受保護的情況下也就表示只有自己本身類別與繼承本身類別的物件可取用這些變數,而至於外部想取用與設定本身Class裡的變數,例如被其他類別生成使用,此種情況下其他類別是無法直接取得變數,須對每個相對應變數個別提供 get & set 給外部呼叫並回傳。而此種寫法你需提供外部取得的變數越多,這種非必要的程始碼也就越加肥大不利於閱讀,因此在Objective C裡提供了@Property的宣告和@Synthesize的對應來簡化前面提到的瑣碎動作。

@interface

#import <Foundation/Foundation.h>

@interface ProAndSyn : NSObject{
   int num_1;
   int num_2;
   int num_3;
}
@property int num_1;
@property int num_2;

-(void)setNum_3:(int)num;
-(int)getNum_3;
-(int)sumNumber;

@end

  • 比較宣告了三個變數上的差別,num_1與num_2在宣告區外都個別在多定義了一個宣告的動作 “@property  型別  變數名"。
  • property屬性的宣告意即將宣告過的變數在編譯時自動加上 get & set方法供外部提取。
  • 而對照num_3並沒有在宣告變數的區塊外再加上@property屬性的定義,所以如果num_3要提供外部使用,我們得土法煉鋼的手動加上 get & set 方法供給外部提取。

@implementation

#import "ProAndSyn.h"

@implementation ProAndSyn

@synthesize num_1;
@synthesize num_2;

-(void)setNum_3:(int)num
{
   num_3 = num;
}
-(int)getNum_3
{
   return num_3;
}
-(int)sumNumber
{
   return num_1 + num_2 + num_3;
}

@end
  • 剛剛在.h檔內曾經被定義為@property的變數在.m實作檔必定要加上對應的@synthesize,各位鄉親請注意,這個步驟將會在編譯時幫你生成每個對應變數的 getter & setter 方法。
  • 往下看看num_3這個變數的下場,在.h檔內並未將他定義為@property,所以在實作的時候,我們得手動寫兩個方法去為num_3這個變數提供外部的 getter & setter 。

@main

#import <Foundation/Foundation.h>
#import "ProAndSyn.h"

int main(int argc, const char * argv[])
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   ProAndSyn *proAndSyn = [[[ProAndSyn alloc] init]autorelease];
   [proAndSyn setNum_1:10];
   [proAndSyn setNum_2:20];
   [proAndSyn setNum_3:30];

   NSLog(@"%d 與 %d 與 %d 的加總為: %d",[proAndSyn num_1], [proAndSyn num_2], [proAndSyn getNum_3], [proAndSyn sumNumber]);

   proAndSyn.num_1 = 30;
   proAndSyn.num_2 = 50;
   [proAndSyn setNum_3:20];

   NSLog(@"%d 與 %d 與 %d 的加總為: %d",proAndSyn.num_1, proAndSyn.num_2, [proAndSyn getNum_3], [proAndSyn sumNumber]);

   [pool drain];
}

®輸出結果 :

 10 與 20 與 30 的加總為: 60
 30 與 50 與 20 的加總為: 100
  • 該變數被定義為@property屬性後,取得屬性變數的寫法規範如下:
  1. setter : 將該變數開頭改為大寫並在變數名前加上set。如屬性變數名稱為 num_1″,而取用setter時就會變成這樣 -> [代表物件 setNum_1]。
  2. getter : 外部欲取得屬性變數無須更動任何變數名稱,以num_1為例寫法如下 -> [代表物件 num_1]。
  • 在上述程式碼中看到了’.’運算子,這也是@property的get&set另一種表現,這種呈現方法又更簡潔了一些,不過運行結果是一樣的。無論是getter還是setter都可直接用’.’運算子對變數做取值或改變,但這對Objective C只不過是個捷徑,本質編譯過程上還是會轉移到上述的寫法。

● 繼承 (Inheritance)

繼承是物件導向的一個重要環節,最主要是提供軟體元件的可重複使用性,藉此提升開發效率。仔細回想前面教學的每一個Class物件應用,其實我們一直以來都繼承NSObject,舉例來說,因為它你的物件得以有init初始化方法。繼承老生常談的解釋方法為:因為老爸有的東西,孩子繼承了父親的一切,即可享有老子的一切,當然孩子還是可以將自身能力(Class)擴充。以下對繼承做簡易的示範:

@interface

#import <Foundation/Foundation.h>

@interface BaseObject : NSObject{
   int posX;
   int posY;
}
@property int posX, posY;

-(void)showPositionXY;

@end

  • 宣告一個物體基底類別,此物體有兩個基本屬性變性 : 座標X & 座標Y。
  • 於此提供一個顯示座標資訊的方法。

@implementation

#import "BaseObject.h"

@implementation BaseObject

@synthesize posX, posY;

-(void)showPositionXY
{
   NSLog(@"posX : %d posY : %d",posX, posY);
}

@end
  • 定義屬性變數@synthesize讓程式編譯時產生getter & setter,並且實作出.h檔定義的方法。
  • 到此為止跟之前學到的基本類別建立並沒有什麼不一樣的地方。

@interface

#import "BaseObject.h"

@interface Rectangle : BaseObject{
   int width;
   int height;
}
@property int width;
@property int height;

-(void) showArea;
-(void)showObjectAllInfo;

@end
  • 再新增一個類別Rectangle代表長方型物件,此物件跟以往繼承NSObject不一樣,改為繼承我們剛剛建立的物體基底類別BaseObject,而NSObject本身有繼承自NSObject,所以Rectangle同時也保有NSObject物件的所有特性。
  • 此類別繼承自BaseObject物體基底物件,延伸成為一個長方形物件,所以我們再Rectangle這個類別裡有多宣告了兩個客制化的屬性變數:寬&高,並新增兩個方法顯示訊息資訊。

@implementation

#import "Rectangle.h"

@implementation Rectangle

@synthesize width, height;

-(void) showArea
{
   NSLog(@"Rectangle Area : %d", width * height);
}
-(void)showObjectAllInfo
{
   NSLog(@"posX: %d posY: %d width: %d height: %d",posX, posY, width, height);
}

@end
  • 此實作檔需要注意的地方再showObjectAllInfo這個方法裡準備要秀出的資訊包含了非本身類別內的屬性變數-> posX & posY,由此可證明了剛剛提到的論調,Rectangle繼承了BaseObject當然理應擁有其所有的一切,恰恰posX & posY就是BaseObject的屬性成員,Rectangle也就順理成章可以取用這些資源。

@main

#import <Foundation/Foundation.h>
#import "Rectangle.h"

int main(int argc, const char * argv[])
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   Rectangle *rectangle = [[[Rectangle alloc] init]autorelease];
   [rectangle setPosX:30];
   [rectangle setPosY:100];
   [rectangle showPositionXY];

   [rectangle setWidth:50];
   [rectangle setHeight:80];
   [rectangle showArea];

   [rectangle showObjectAllInfo];

   [pool drain];
}

®輸出結果 :

 posX : 30 posY : 100
 Rectangle Area : 4000
 posX: 30 posY: 100 width: 50 height: 80
  • 一樣來到了main主函示來生成Rectangle 並一一show出所有的資訊驗證剛剛所談的一切,再此我們只需要生成Rectangle物件即可證明繼承特性,再次強調,因為Rectangle繼承了BaseObject,也就是擁有了他的所有資源,因此原本存在在BaseObject裡的所有方法Rectangle也能直接調用。

● 多型 (Polymorphism) & 覆寫 (Override)

多型與繼承在任何物件導向語言都是緊緊結合在一起的。多型的意義在於物件能夠在執行階段依照不同情況變換資料型態,換句話說,多型是指一個物件參考能夠在不同環境下,擁有不同的特性。一個物件要實現多型可透過實作多個繼承(包含向下繼承 例如: A繼承B,B又繼承C)或介面來實現父類別內已存在相同名稱的方法內容,多數語言為了可讀性會使用Override注義於開頭宣告供辨識,Override即為將父類別原本的方法取代。舉個簡單的例子來說,A物件定義了一個"reset"方法名稱,此時B物件繼承了A物件,並在B物件本身又宣告一個一模一樣的"reset"方法名稱,而此種情況下當有人生成了B物件欲使用 “reset"方法時,"reset"方法只會執行B物件複寫過內容,而對B物件來說A物件的"reset"依然存在,如必要連A物件內的方法一起執行,此時B物件應呼叫 [super reset] 來請他老爸執行他的"reset"方法。以下示範程式實例 :

@interface

#import <Foundation/Foundation.h>

@interface BaseObject : NSObject{
 int length, width, height;

}
@property int length, width, height;

-(double) getArea;

@end
  • 一樣的我們新增一個做為物體基底的類別BaseObject,並宣告了四種關於尺寸的屬性變數 : 長,寬,高,半徑,且提供一個方法供外部提取物體體積數據。

@implementation

#import "BaseObject.h"

@implementation BaseObject

@synthesize length, width, height;

-(double)getArea
{
   return length * width;
}

@end
  • 實作getArea方法,但僅僅做最簡單的長方形面積運算並回傳。

@interface

#import "BaseObject.h"

@interface Rectangle : BaseObject{

}

-(double) getArea;

@end
  • 新增Rectangle長方形物件並繼承BaseObject物體基底,在這裡可以看到Rectangle宣告了跟他老爸一模一樣的方法。一經宣告後,此時父類別BaseObject中的getArea方法已然被覆寫。

@implementation

#import "Rectangle.h"

@implementation Rectangle

-(double) getArea
{
   return [super getArea];
}

@end
  • 剛才提到說一經宣告父類別的相同方法及被取代,但僅僅對於外部呼叫Rectangle而言是如此,而父類別的所有一切對Rectangle來說一直都是存在的,如欲調用父類別以存在的方法或屬性只需以關鍵字super做為呼叫,隨後加上欲呼叫的屬性或法方即可。此段的用意在於簡單證明父類別的調用,長方形的面積計算方式是 長 * 寬 ,而恰恰與父類別BaseObject的getArea方法預設計算方式相同,所有簡而化之直接呼叫父類別的方法計算後回傳給,接收到值後直接在回傳給呼叫Rectangle物件getArea的人。

@interface

#import "BaseObject.h"
#import "BaseObject.h"

@interface Circle : BaseObject{
  int radius;
}
@property int radius;

-(double) getArea;

@end
  • 建構一Circle圓形物件並覆寫其父類別方法,並在此物體物件內新增一獨有屬性radius(半徑)。 PS:這簡簡單單的新增一獨有變數其實就是再說明多型的特性,雖然圓形也是物體的種類,但他多了其他物體不需要的屬性"半徑",但還是與其它物體形態的物件有著一樣的屬性基底。

@implementation

#import "Circle.h"

@implementation Circle

@synthesize radius;

-(double) getArea
{
  return pow(radius,2) * M_PI;
}

@end
  • pow是C語言的基本數學函式,意在做開平方的動作,其用法解釋為 : pow(半徑 , 次方)。
  • M_PI為objective c的數學定義(define),僅僅代表3.14159….的數。 PS : 圓面積算法為 : 半徑*半徑*PI。
  • 在Circle類別中已結結實實的把原本父類別的方法內容給複寫掉了,此時外部調用getArea時已不會回傳原本老爸定義的 長*寬 的內容,而是回傳圓面積的算式運算結果。

@interface

#import "BaseObject.h"

@interface Cube : BaseObject{

}
-(double) getArea;

@end
  • 為了再次證明,我們再生成一個立方體的物件做做實驗。

@implementation

#import "Cube.h"

@implementation Cube

-(double) getArea
{
   return length * width * height;
}

@end
  • 以上三個變數都是由BaseObject這個聰明的老爸傳承下來,因為當時老爸覺得大家同是物體類,所以一定會有長寬高這三樣基本條件,而現在Cube(立方體)什麼都不用做,直接取用老子所定義的三個屬性變數即可算出體積。至於體積的公式…..去問國小老師吧 !!

@main

#import <Foundation/Foundation.h>
#import "Rectangle.h"
#import "Circle.h"
#import "Cube.h"

int main(int argc, const char * argv[])
{
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   Rectangle *rectangle = [[[Rectangle alloc] init] autorelease];
   [rectangle setLength:50];
   [rectangle setWidth:10];
   NSLog(@"Rectangle Area : %.2f",[rectangle getArea]);

   Circle *circle = [[[Circle alloc] init] autorelease];
   [circle setRadius:10];
   NSLog(@"Circle Area : %.2f",[circle getArea]);

   Cube *cube = [[[Cube alloc] init] autorelease];
   [cube setLength:50];
   [cube setWidth:10];
   [cube setHeight:100];
   NSLog(@"Cube Measure : %.2f",[cube getArea]);

   [pool drain];
}

®輸出結果 :

 Rectangle Area : 500.00
 Circle Area : 78.54
 Cube Measure : 50000.00
  • 以上分別生成了三個已繼承了物體基底物件的客制化物體物件,並依照自身特性初始內容屬性變數值,例如圓形算出面積只需要半徑,故圓形物件只需設定radius(半徑)的值即可。
  • 以上結果如我們預期發揮了多型與繼承的特性,提升程式書寫上的統一性,更少了一堆不必要的重複性程式碼。
廣告

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

%d 位部落客按了讚: