在介紹多態(tài)之前,我們來回顧下面向?qū)ο蟮娜齻€特性,封裝、繼承、多態(tài)
封裝:就是對類中的一些方法、字段進行保護,不被外界訪問,有一種權(quán)限控制功能,例如OC中有@public、@protected、@private、@package,默認使用@private
繼承:為了代碼的重用,子類可以繼承父類的方法和變量
多態(tài):多態(tài)是指同一操作作用于不同的對象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。它是面向?qū)ο蟪绦蛟O計(OOP)的一個重要特征,動態(tài)類型能使程序直到執(zhí)行時才確定對象的所屬類,其具體引用的對象在運行時才能確定。動態(tài)綁定能使程序直到運行時才確定調(diào)用對象的實際方法。
C++中的多態(tài)性具體體現(xiàn)在運行和編譯兩個方面,編譯時多態(tài)是靜態(tài)多態(tài)(重載、模版),在編譯時就可以確定對象使用的形式,運行時多態(tài)是動態(tài)多態(tài)(虛函數(shù),抽象類,覆蓋)。
C++使用虛函數(shù)(虛函數(shù)表)來實現(xiàn)動態(tài)綁定,當基類對象的指針(或引用)指向派生類的對象時候,實際調(diào)用的是派生類相應的函數(shù)。
Objective-c 是動態(tài)語言,所以它具有動態(tài)類型和動態(tài)綁定的特性。Objective-c系統(tǒng)總是跟蹤對象所屬的類。對于類型的判斷和方法的確定都是在運行時進行。那Objective-c是怎么樣實現(xiàn)多態(tài)特性的呢?
二 Objective-c多態(tài)
首先看下面代碼
draw.h文件
@interfaceDraw :NSObject
@property(nonatomic,strong)NSString*name;
- (void) Print;
- (void) draw;
@end
draw.m文件
#import"Draw.h"
@implementationDraw
@synthesizename;
- (id) init
{
if(self= [superinit])
{
self.name=@"Draw Demo";
}
returnself;
}
- (void) draw
{
NSLog(@"Draw::draw.......");
}
- (void) Print
{
NSLog(@"i am? %@.",self.name);
}
@end
cricle.h文件
#import"Draw.h"
@interfaceCircle :Draw
@end
circle.m文件
#import"Circle.h"
@implementationCircle
- (void) draw
{
NSLog(@"%@:draw circle",self.name);
}
@end
Retangle.h文件
#import"Draw.h"
@interfaceRetangle :Draw
@end
Retangle.m文件
#import"Retangle.h"
@implementationRetangle
- (void) draw
{
[superdraw];//通過super關(guān)鍵字可以調(diào)用基類的draw函數(shù)
NSLog(@"%@:draw retangle",self.name);
}
@end
我們定義了一個Draw基類,里面有一個數(shù)據(jù)成員name,和兩個函數(shù)成員draw和Print,Circle和Retangle是從Draw派生的兩個類,他們重寫了基類Draw的draw方法。
代碼使用
Draw* base = [[Circlealloc]init];
[basedraw];//draw circle
NSLog(@"address:%@",base);
base = [[Retanglealloc]init];
[basedraw];//draw retangle
NSLog(@"address:%@",base);
[basePrint];
輸出結(jié)果
2014-04-09 15:34:41.648 duotaidemo[7718:303] Draw Demo:draw circle
2014-04-09 15:34:41.673 duotaidemo[7718:303] address:
2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw::draw.......
2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw Demo:draw retangle
2014-04-09 15:34:41.675 duotaidemo[7718:303] address:
2014-04-09 15:34:41.676 duotaidemo[7718:303] i am? Draw Demo.
使用基類的指針分別指向創(chuàng)建的兩個派生類對象,然后分別調(diào)用各自的draw函數(shù),通過輸出結(jié)果可以發(fā)現(xiàn)他們調(diào)用的是各自的draw方法。由于Retangele沒有重寫基類的Print函數(shù),所有使用[base Print]調(diào)用的是基類的方法。同時通過address的輸出發(fā)現(xiàn)base指向了兩個不同的對象。
小結(jié):
1.與C++ 的多態(tài)相比,在Objective-c中是沒有virtual關(guān)鍵字的,默認情況下只要子類重寫了父類的方法就實現(xiàn)了覆蓋(這一點和java類似),在Objective-c中同一類中的函數(shù)是不能被重載的。
2.在Objective-c中,通過super關(guān)鍵字可以調(diào)用基類的函數(shù),這個在C++中是沒有的,在C++中可通過作用域運算符訪問基類成員。
除了上面的調(diào)用方式外,我們也可以這樣:
idbase = [[Circlealloc]init];
[basedraw];//draw circle
NSLog(@"address:%@",base);
base = [[Retanglealloc]init];
[basedraw];//draw retangle
NSLog(@"address:%@",base);
[basePrint];
其輸出結(jié)果和上面是一樣的
既然Objective-c中沒有像C++一樣的虛函數(shù)表,那它的多態(tài)是怎么實現(xiàn)的?它的類型系統(tǒng)是怎么樣構(gòu)建起來的呢?繼續(xù)往下看吧!
三 ?類對象
雖然Objective-c沒有虛函數(shù)表,但是它有一個根類NSObject,下面讓我們探究一下這個根類是個什么東東。
objc.h文件中關(guān)于NSObject的定義
@interfaceNSObject
{
Class isaOBJC_ISA_AVAILABILITY;
}
typedefstructobjc_class*Class;
typedefstructobjc_object {
Class isa;
} *id;
typedefstructobjc_selector*SEL;
typedefid(*IMP)(id,SEL, ...);
詳見:http://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc.h
通過上面的定義我們可以知道以下事實:
1.Class isa 是NSObject類的第一個數(shù)據(jù)成員。
2.Class 是一個指針,它指向一個objc_class的結(jié)構(gòu)體。
3.id 類型是一個指針,它指向一個objc_object的結(jié)構(gòu)體,該結(jié)構(gòu)體只有一個成員即Class isa;
4.id 類型是一個指針,它指向一個存有objc_class的結(jié)構(gòu)對象的指針的指針。
3.1 isa介紹
以下是蘋果官方文檔對isa的介紹說明:
Every object is connected to the run-time system through its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.
實例變量是通過isa成員鏈接到運行時系統(tǒng)環(huán)境中的,任意NSObject的子類都會繼承NSObject的isa成員,而且當NSObject的子類實例化對象時,isa實例變量永遠是對象的第一個實例變量。isa指向該對象的類對象,它是實例和類對象連接的橋梁。
實例變量和類對象的關(guān)聯(lián),如下圖所示:
下面是類對象(objc_class)的結(jié)構(gòu)體
structobjc_class {
Class isa;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* metaclass */? ? ? ? ? ? ? ? Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* 父類的地址 */constchar*name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? 類名稱? */longversion? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? 版本? ? */longinfo? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? 類信息? */longinstance_size? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? 實例大小? */structobjc_ivar_list *ivars? ? ? ? ? ? ? ? ? ? /*? 實例參數(shù)列表*/structobjc_method_list **methodLists/*? 方法列表? */structobjc_cache *cache? ? ? ? ? ? ? ? ? ? ? /*? 方法緩存? */structobjc_protocol_list *protocols/*? protocol鏈表*/
} ;
在Objective-C中類也是一種對象,而且在程序運行時一直存在。類對象是一個根據(jù)類定義生成的一個結(jié)構(gòu)體,里面存儲了類的基本信息, 如:類的大小,類的名稱,類的版本以及消息與函數(shù)的映射表等信息。類對象所保存的信息在程序編譯時確定,在程序啟動 時加載到內(nèi)存中。
3.2 id介紹
由上面的定義我們知道,id類型是一個指向類對象的指針的指針。在Objective-c中,id類型是一種通用的指針類型,id類型可以用來指向?qū)儆谌魏晤惖膶ο?只要該對象是屬于NSObject即成體系)。
id類型的使用如下圖所示:
在使用id類型的時候要注意:
1. id類型本事是一個指針類型,在使用時就不用加*號了,例如上面的例子idbase = [[Circlealloc]init];
2.id類型是通用指針類型,弱類型,編譯時不進行類型檢查
Objective-C可以將對象分為id類型和靜態(tài)類型,如果不涉及到多態(tài),盡量使用靜態(tài)類型。
在上的例子中我們使用了兩種方式來調(diào)用派生類函數(shù),第一種使用的即使靜態(tài)類型,第二種使用的是id動態(tài)類型。在寫代碼時候,盡量使用靜態(tài)類型,靜態(tài)類型可更好的在編譯階段而不是運行階段指出錯誤,同時能夠提高程序的可讀性。
四 小結(jié)
實例變量中isa成員用于保持其類對象在內(nèi)存的地址,類對象對于所有實例來說在內(nèi)存中只有一份副本,任何一個實例都可以通過 isa成員,訪問類對象所保持的類的信息,isa成員可以通過類對象獲得當前實例可以訪問的消息列表,以及消息對應的函數(shù)地址。
Objecive-c使用類對象的形式來實現(xiàn)運行多態(tài),每個對象都保存其類對象的地址,類對象中保存了類的基本信息。類對象是進行動態(tài)創(chuàng)建(反射),動態(tài)識別,消息傳遞等機制的基礎(chǔ)。
那么上面的程序中,函數(shù)的調(diào)用過程時怎么樣利用類對象的呢?