多態這個其它語言也有。動態類型有類似的,但不完全相同。
動態綁定別的語言也有類似,但沒有objective-c用的這么多。
多態能夠使來自不同類的對象定義相同名稱的方法。
動態類型能使程序直到執行時才確定對象所屬的類。
動態綁定則能使程序直到執行時才確定實際要調用的對象方法。
多態性是指在父類中定義的成員變量和方法被子類繼承之后, 可以具有不同的數據類型或表現出不同的行為。?
這使得同一個變量和方法在父類及其各個子類中具有不同的表現形式。?
簡單點說:相同的名稱,不同的類,系統總是攜帶有關“一個對象屬于哪個類”這樣的信息。
該信息能使系統在運行時做出這些關鍵性的決定,而不是在編譯時。
這種使不同的類共享相同方法名稱的能力就稱為多態。
我們通過一個例子理解什么是多態:
例如: “幾何圖形” 類的“繪圖” 方法, 在它的子類“橢圓形” 和“三角形” 中也都有“繪圖” 的方法,
但是“繪圖” 方法功能都不同。
幾何圖形類圖
Graphics(幾何圖形) 類是Ellipse(橢圓形) 類和Triangle(三角形) 類的父類,?
Ellipse和Triangle重寫了onDraw方法。

Graphics.h文件
Graphics類在h文件中定義onDraw, Graphics類應該無法實現這
個onDraw, 這是因為在幾何圖形中是無法知道要繪制的是橢
圓形和三角形, 這個方法應該是類似Java中的抽象方法, 或者是
C++中的虛方法。
@interface Graphics : NSObject {
}
-(void) onDraw;
@end
Ellipse類的h和m文件
~~~
#import???
#import?"Graphics.h"??
??
@interface?Ellipse?:?Graphics?{??
??
}??
??
@end??
~~~
~~~
#import?"Ellipse.h"??
??
??
@implementation?Ellipse??
??
-(void)onDraw?{??
????NSLog(@"繪制橢圓形");??
}??
??
@end??
~~~
Triangle類的h和m文件
~~~
#import???
#import?"Graphics.h"??
??
@interface?Triangle?:?Graphics?{??
??
}??
??
@end??
~~~
~~~
#import?"Triangle.h"??
??
??
@implementation?Triangle??
??
-(void)onDraw?{??
????NSLog(@"繪制三角形");??
}??
??
@end??
~~~
#### 調用的main函數
~~~
#import???
#import?"Graphics.h"??
#import?"Ellipse.h"??
#import?"Triangle.h"??
??
int?main?(int?argc,?const?charchar?*?argv[])?{??
??????
????Graphics?*graphics;??
????graphics?=?[[Ellipse?alloc]?init];??
????[graphics?onDraw];??
????[graphics?release];??
??????
????graphics?=?[[Triangle?alloc]?init];??
????[graphics?onDraw];??
????[graphics?release];??
??????
????return?0;??
}??
~~~
運行結果:
繪制橢圓形
繪制三角形
動態類型和動態綁定
id 是泛類型 (generic data type), 可以用來存放各種類型的對象,?
使用 id 也就是使用“動態類型”。?
上面的例子改寫下:
~~~
int main (int argc, const char * argv[]) {
id graphics;
graphics = [[Ellipse alloc] init];
[graphics onDraw];
[graphics release];
graphics = [[Triangle alloc] init];
[graphics onDraw];
[graphics release];
return 0;
}
~~~
把Graphics *改成id類型, 程序運行的結果沒有任何影響。
由于動態類型的關系,id 在執行時,?
Objective-C 的執行環境會找出該 id 所代表的原來類型,?
所以根本沒有所謂的轉型。?
id 并不是自動的轉換成 Ellipse和 Triangle的父類,而是在執行期間,?
由執行環境辨認出 id 實際代表的類型為Ellipse還是Triangle。?
因此在這個例子中id 與Graphics沒有任何關系。
務必注意,聲明中并沒有使用星號。
我們再舉一個例子來說明動態的概念:
矢量和標量是數學和物理學應用中經常用到的兩個概念;
矢量” 即有方向和大小的量, 如物理學中的“力” 。?
標量為沒有方向只有大小量, 如物理學中的“功”。
下面從程序的角度來實現這兩個概念。
先定義兩個類: “矢量” 和“標量” 類。?

Vector.h文件
~~~
#import???
??
??
@interface?Vector?:?NSObject?{??
????double?vec1;??
????double?vec2;??
}??
??
@property?double?vec1,vec2;??
-(void)print;??
-(void)setVec1:(double)v1?andVec2:(double)?v2;??
-(Vector?*)add:(Vector?*)v;??
??
@end??
~~~
Vector.m文件
~~~
#import?"Vector.h"??
??
??
@implementation?Vector??
@synthesize?vec1,vec2;??
-(void)?setVec1:(double)?v1?andVec2:(double)v2?{??
????vec1?=?v1;??
????vec2?=?v2;??
}??
-(Vector?*)add:(Vector?*)v?{??
????Vector?*result?=?[[Vector?alloc]?init];??
????[result?setVec1:vec1?+?[v?vec1]?andVec2:?vec2?+?[v?vec2]];??
????return?result;??
}??
??
-(void)print?{??
????NSLog(@"%g,??%g",vec1,vec2);??
}??
??
@end??
~~~
Scalar.h文件
~~~
#import???
??
??
@interface?Scalar?:?NSObject?{??
????double?scal;??
}??
@property?double?scal;??
-(void)print;??
-(void)setScal:(double)sval;??
-(Scalar?*)add:(Scalar?*)s;??
??
@end??
~~~
Scalar.m文件
~~~
#import?"Scalar.h"??
??
??
@implementation?Scalar??
??
@synthesize?scal;??
??
-(void)print?{??
????NSLog(@"%g",?scal);??
}??
??
-(void)setScal:(double)sval?{??
????scal?=?sval;??
}??
??
-(Scalar?*)add:(Scalar?*)s?{??
????Scalar?*result??=?[[Scalar?alloc]?init];??
????[result?setScal:scal?+?[s?scal]];??
????return?result;??
}??
~~~
調用的main函數
~~~
#import???
??
#import?"Vector.h"??
#import?"Scalar.h"??
??
int?main?(int?argc,?const?charchar?*?argv[])?{??
??????
????Scalar?*scA?=[[Scalar?alloc]?init];??
????Scalar?*scB?=[[Scalar?alloc]?init];??
??
????Vector?*vecA?=[[Vector?alloc]?init];??????
????Vector?*vecB?=[[Vector?alloc]?init];??
??????
????id?scAandB;??
????id?vecAandB;??
??
????[scA?setScal:?10.5];??
????[scB?setScal:?13.1];??
????[vecA?setVec1:?3.2?andVec2:?4.7];??
????[vecB?setVec1:?32.2?andVec2:?47.7];??
??
????[vecA?print];??
????NSLog(@"?+?");??
????[vecB?print];??
????NSLog(@"?=?");??
????vecAandB?=?[vecA?add:?vecB];??
????[vecAandB?print];??
????[scA?print];??
????NSLog(@"?+?");??
????[scB?print];??
????NSLog(@"?=?");??
????scAandB?=?[scA?add:?scB];??
????[scAandB?print];??
??????
????[scA?release];??
????[scB?release];??
????[scAandB?release];??
????[vecA?release];??
????[vecB?release];??
????[vecAandB?release];??
??????
????return??0;??
}??
~~~
運行結果:
~~~
3.2, 4.7
+
32.2, 47.7
=
35.4, 52.4
10.5
+
13.1
=
23.6
~~~
#### 代碼說明:
scAandB和vecAandB對象都是動態類型, 都可調用以"print"和"add"方法。
???
注意:
雖然id類型可以任何類型的對象,但是不要濫用 ,
如果能夠確定對象數據類型時候, 要使用“靜態類型” ,?
為什么要使用靜態類型:
1)將一個變量定義為特定類的對象時,使用的是靜態類型。
“靜態”指的是對存儲在變量中對象的類型進行顯示聲明。
這樣存儲在這種形態中的對象的類是預定義的,也就是靜態的。
使用靜態類型時,編譯盡可能確保變量的用法在程序中始終保持一致。
編譯器能夠通過檢查來確定應用于對象的方法是由該類定義的還是由該類繼承的,否則它將顯示警告信息。
也就是說“靜態類型” 在編譯階段檢查錯誤, 而不是在執行階段。?
2)使用靜態類型的另一個原因是程序可讀性好。。
動態類型的參數和返回類型:
如何使用動態類型來調用一個方法,需要注意如下規則:
如果在多個類中實現名稱相同的方法,那么每個方法都必須符合各個參數的類型和返回值類型。
這樣編譯器才能為消息表達式生成正確的代碼。編譯器會對它所遇到的每個類聲明執行一致性檢查。
當一個方法選取對象作為它的參數,而另一個方法選取浮點數作為參數時,
或者一個方法以對象作為返回值,而另一個以整型數作為返回值。
編譯器可能生成不正確的代碼來向方法傳遞參數或處理返回值。
處理動態類型的方法:
~~~
-(BOOL)?isKindOf:class-object(判斷對象是否是class-object或其子類的成員) ?-(BOOL)?isMenberOfClass:class-object(判斷對象是否是class-object的成員) ?-(BOOL)?respondsToSelector:selector(判斷對象是否能夠響應selector所指定的方法) ?+(BOOL)?instancesRespondToSelector:selector(判斷指定的類實例是否能響應selector所指定的方法) ?+(BOOL)?isSubclassOfClass:class-object(判斷對象是否是指定類的子類) ?-(id)?performSelector:selector(應用selector指定的方法) ?-(id)?performSelector:selector?withObject:object(應用selector指定的方法,傳遞參數object) ??-(id)?performSelector:selector?withObject:object1?withObject:object2(應用selector指定的方法,傳遞參數object1和object2
~~~
可以對一個方法名應用@selector指令。
例如:@selector (alloc)為名為alloc的方法生成一個SEL類型的值,該方法是從NSObject類繼承的。
記住,測試包含繼承的方法,并不是只測試直接定義在類中的方法。
performSelector:方法和它的變體允許你向對象發送消息,這個消息可以是存儲在變量中的selector。
在iOS中,respondsToSelector:方法廣泛用于實現委托(delegation)的概念。
為了讓系統能夠檢查你確實實現了特定的方法,
使用respondsToSelector:判斷是否可以將事件的處理委托給你的方法。
如果你沒有實現這個方法,它會自己處理該事件,按定義的默認行為來執行。
#### 使用@try處理異常
@try:如果塊中的某一語句拋出異常,執行不會終止,而是立即跳到@catch塊繼續執行
@catch:處理異常,可行的執行順序是記錄出錯信息,清除和終止執行。
@finally:使用@finally塊包含是否執行拋出異常的@try塊中的語句代碼;
@throw:允許你拋出自己的異常,
這些概念和java的或者其它語言差不多。
一般來說,需要考慮更好的編程實踐,
應該在錯誤發生前做系統的全面的覆蓋性測試,而不是在錯誤發生后捕獲異常。
拋出異常會使用大量的系統資源,Apple反對非必要的使用異常。