在介紹多態(tài)之前彻桃,我們來回顧下面向?qū)ο蟮娜齻€特性坛善,封裝、繼承邻眷、多態(tài)
封裝:就是對類中的一些方法眠屎、字段進(jìn)行保護(hù),不被外界訪問肆饶,有一種權(quán)限控制功能改衩,例如OC中有@public、@protected驯镊、@private葫督、@package,默認(rèn)使用@private
繼承:為了代碼的重用,子類可以繼承父類的方法和變量
多態(tài):多態(tài)是指同一操作作用于不同的對象板惑,可以有不同的解釋橄镜,產(chǎn)生不同的執(zhí)行結(jié)果。它是面向?qū)ο蟪绦蛟O(shè)計(OOP)的一個重要特征冯乘,動態(tài)類型能使程序直到執(zhí)行時才確定對象的所屬類洽胶,其具體引用的對象在運(yùn)行時才能確定。動態(tài)綁定能使程序直到運(yùn)行時才確定調(diào)用對象的實際方法裆馒。
C++中的多態(tài)性具體體現(xiàn)在運(yùn)行和編譯兩個方面姊氓,編譯時多態(tài)是靜態(tài)多態(tài)(重載丐怯、模版),在編譯時就可以確定對象使用的形式他膳,運(yùn)行時多態(tài)是動態(tài)多態(tài)(虛函數(shù)响逢,抽象類,覆蓋)棕孙。
C++使用虛函數(shù)(虛函數(shù)表)來實現(xiàn)動態(tài)綁定舔亭,當(dāng)基類對象的指針(或引用)指向派生類的對象時候,實際調(diào)用的是派生類相應(yīng)的函數(shù)蟀俊。
Objective-c 是動態(tài)語言钦铺,所以它具有動態(tài)類型和動態(tài)綁定的特性。Objective-c系統(tǒng)總是跟蹤對象所屬的類肢预。對于類型的判斷和方法的確定都是在運(yùn)行時進(jìn)行矛洞。那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)鍵字的祝辣,默認(rèn)情況下只要子類重寫了父類的方法就實現(xiàn)了覆蓋(這一點和java類似)贴妻,在Objective-c中同一類中的函數(shù)是不能被重載的。
2.在Objective-c中蝙斜,通過super關(guān)鍵字可以調(diào)用基類的函數(shù)名惩,這個在C++中是沒有的,在C++中可通過作用域運(yùn)算符訪問基類成員孕荠。
除了上面的調(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成員鏈接到運(yùn)行時系統(tǒng)環(huán)境中的,任意NSObject的子類都會繼承NSObject的isa成員,而且當(dāng)NSObject的子類實例化對象時,isa實例變量永遠(yuǎn)是對象的第一個實例變量慌植。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中類也是一種對象,而且在程序運(yùn)行時一直存在蝶柿。類對象是一個根據(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類型是通用指針類型俏橘,弱類型,編譯時不進(jìn)行類型檢查
Objective-C可以將對象分為id類型和靜態(tài)類型施逾,如果不涉及到多態(tài)敷矫,盡量使用靜態(tài)類型例获。
在上的例子中我們使用了兩種方式來調(diào)用派生類函數(shù)汉额,第一種使用的即使靜態(tài)類型,第二種使用的是id動態(tài)類型榨汤。在寫代碼時候蠕搜,盡量使用靜態(tài)類型,靜態(tài)類型可更好的在編譯階段而不是運(yùn)行階段指出錯誤收壕,同時能夠提高程序的可讀性妓灌。
四 小結(jié)
實例變量中isa成員用于保持其類對象在內(nèi)存的地址,類對象對于所有實例來說在內(nèi)存中只有一份副本蜜宪,任何一個實例都可以通過 isa成員虫埂,訪問類對象所保持的類的信息,isa成員可以通過類對象獲得當(dāng)前實例可以訪問的消息列表圃验,以及消息對應(yīng)的函數(shù)地址掉伏。
Objecive-c使用類對象的形式來實現(xiàn)運(yùn)行多態(tài),每個對象都保存其類對象的地址,類對象中保存了類的基本信息斧散。類對象是進(jìn)行動態(tài)創(chuàng)建(反射)供常,動態(tài)識別,消息傳遞等機(jī)制的基礎(chǔ)鸡捐。
那么上面的程序中栈暇,函數(shù)的調(diào)用過程時怎么樣利用類對象的呢?