版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系我們

[科普中國]-object-c

科學(xué)百科
原創(chuàng)
科學(xué)百科為用戶提供權(quán)威科普內(nèi)容,打造知識(shí)科普陣地
收藏

Objective-C是一種通用、高級、面向?qū)ο蟮木幊陶Z言。它擴(kuò)展了標(biāo)準(zhǔn)的ANSI C編程語言,將Smalltalk式的消息傳遞機(jī)制加入到ANSI C中。當(dāng)前主要支持的編譯器有GCC和Clang(采用LLVM作為后端)。

Objective-C的商標(biāo)權(quán)屬于蘋果公司,蘋果公司也是這個(gè)編程語言的主要開發(fā)者。蘋果在開發(fā)NeXTSTEP操作系統(tǒng)時(shí)使用了Objective-C,之后被OS X和iOS繼承下來。現(xiàn)在Objective-C與Swift是OS X和iOS操作系統(tǒng)、及與其相關(guān)的API、Cocoa和Cocoa Touch的主要編程語言。

歷史Objective-C 主要由Stepstone公司的布萊德·考克斯(Brad Cox)和Tom Love在 1980 年代發(fā)明。

1981年 Brad Cox 和 Tom Love 還在ITT 公司技術(shù)中心任職時(shí),接觸到了SmallTalk語言。Cox 當(dāng)時(shí)對軟件設(shè)計(jì)和開發(fā)問題非常感興趣,他很快地意識(shí)到SmallTalk語言在系統(tǒng)工程構(gòu)建中具有無法估量的價(jià)值,但同時(shí)他和 Tom Love 也明白,當(dāng)前ITT 公司的電子通信工程相關(guān)技術(shù)中,C 語言被放在很重要的位置。

于是 Cox 撰寫了一個(gè) C 語言的預(yù)處理器,打算使 C 語言具備些許 Smalltalk 的本領(lǐng)。Cox 很快地實(shí)現(xiàn)了一個(gè)可用的 C 語言擴(kuò)展,此即為 Objective-C語言的前身。到了 1983 年,Cox 與 Love 合伙成立了 Productivity Products International(PPI)公司,將 Objective-C 及其相關(guān)庫商品化販?zhǔn)郏⒃谥髮⒐靖拿麨镾tepStone。1986年,Cox 出版了一本關(guān)于 Objective-C 的重要著作《Object-Oriented Programming, An Evolutionary Approach》,書內(nèi)詳述了 Objective-C 的種種設(shè)計(jì)理念。

1988年,斯蒂夫·喬布斯(Steve Jobs)離開蘋果公司后成立了NeXT Computer公司,NeXT 公司買下 Objective-C 語言的授權(quán),并擴(kuò)展了著名的開源編譯器GCC使之支持 Objective-C 的編譯,基于 Objective-C 開發(fā)了AppKit與Foundation Kit等庫,作為 NeXTSTEP 的的用戶界面與開發(fā)環(huán)境的基礎(chǔ)。雖然 NeXT 工作站后來在市場上失敗了,但 NeXT 上的軟件工具卻在業(yè)界中被廣泛贊揚(yáng)。這促使 NeXT 公司放棄硬件業(yè)務(wù),轉(zhuǎn)型為銷售NeXTStep(以及OpenStep)平臺(tái)為主的軟件公司。

1992年,自由軟件基金會(huì)的 GNU 開發(fā)環(huán)境增加了對 Objective-C 的支持。1994年,NeXT Computer公司和Sun Microsystem聯(lián)合發(fā)布了一個(gè)針對 NEXTSTEP 系統(tǒng)的標(biāo)準(zhǔn)典范,名為 OPENSTEP。OPENSTEP 在自由軟件基金會(huì)的實(shí)現(xiàn)名稱為GNUstep。1996年12月20日,蘋果公司宣布收購 NeXT Software 公司,NEXTSTEP/OPENSTEP環(huán)境成為蘋果操作系統(tǒng)下一個(gè)主要發(fā)行版本OS X的基礎(chǔ)。這個(gè)開發(fā)環(huán)境的版本被蘋果公司稱為Cocoa。

2005年,蘋果計(jì)算機(jī)雇用了克里斯·拉特納及LLVM開發(fā)團(tuán)隊(duì),clang及LLVM成為蘋果公司在GCC之外的新編譯器選擇,在Xcode4.0之后均采用 LLVM 作為默認(rèn)的編譯器。最新的 Modern Objective-C 特性也都率先在 Clang 上實(shí)現(xiàn)。

語法Objective-C是C語言的嚴(yán)格超集--任何C語言程序不經(jīng)修改就可以直接通過Objective-C編譯器,在Objective-C中使用C語言代碼也是完全合法的。Objective-C被描述為蓋在C語言上的薄薄一層,因?yàn)镺bjective-C的原意就是在C語言主體上加入面向?qū)ο蟮奶匦?。Objective-C的面向?qū)ο笳Z法源于Smalltalk消息傳遞風(fēng)格。所有其他非面向?qū)ο蟮恼Z法,包括變量類型,預(yù)處理器(preprocessing),流程控制,函數(shù)聲明與調(diào)用皆與C語言完全一致。但有些C語言語法合法代碼在objective-c中表達(dá)的意思不一定相同,比如某些布爾表達(dá)式,在C語言中返回值為true,但在Objective-C若與yes直接相比較,函數(shù)將會(huì)出錯(cuò),因?yàn)樵贠bjective-C中yes的值只表示為1.

Hello World這里示范了一個(gè)基礎(chǔ)的Hello World程序?;赬code 4.3.1:

#import int main(int argc, char *argv[]) { @autoreleasepool { NSLog(@"Hello World!"); } return 0;}消息傳遞Objective-C最大的特色是承自Smalltalk的消息傳遞模型(message passing),此機(jī)制與今日C++式之主流風(fēng)格差異甚大。Objective-C里,與其說對象互相調(diào)用方法,不如說對象之間互相傳遞消息更為精確。此二種風(fēng)格的主要差異在于調(diào)用方法/消息傳遞這個(gè)動(dòng)作。C++里類別與方法的關(guān)系嚴(yán)格清楚,一個(gè)方法必定屬于一個(gè)類別,而且在編譯時(shí)(compile time)就已經(jīng)緊密綁定,不可能調(diào)用一個(gè)不存在類別里的方法。但在Objective-C,類別與消息的關(guān)系比較松散,調(diào)用方法視為對對象發(fā)送消息,所有方法都被視為對消息的回應(yīng)。所有消息處理直到運(yùn)行時(shí)(runtime)才會(huì)動(dòng)態(tài)決定,并交由類別自行決定如何處理收到的消息。也就是說,一個(gè)類別不保證一定會(huì)回應(yīng)收到的消息,如果類別收到了一個(gè)無法處理的消息,程序只會(huì)拋出異常,不會(huì)出錯(cuò)或崩潰。

C++里,送一個(gè)消息給對象(或者說調(diào)用一個(gè)方法)的語法如下:

obj.method(argument);Objective-C則寫成:

[obj method: argument];此二者并不僅僅是語法上的差異,還有基本行為上的不同。

這里以一個(gè)汽車類(car class)的簡單例子來解釋Objective-C的消息傳遞特性:

[car fly];典型的C++意義解讀是“調(diào)用car類別的fly方法”。若car類別里頭沒有定義fly方法,那編譯肯定不會(huì)通過。但是Objective-C里,我們應(yīng)當(dāng)解讀為“發(fā)提交一個(gè)fly的消息給car對象”,fly是消息,而car是消息的接收者。car收到消息后會(huì)決定如何回應(yīng)這個(gè)消息,若car類別內(nèi)定義有fly方法就運(yùn)行方法內(nèi)之代碼,若car內(nèi)不存在fly方法,則程序依舊可以通過編譯,運(yùn)行期則拋出異常。

此二種風(fēng)格各有優(yōu)劣。C++強(qiáng)制要求所有的方法都必須有對應(yīng)的動(dòng)作,且編譯期綁定使得函數(shù)調(diào)用非??焖?。缺點(diǎn)是僅能借由virtual關(guān)鍵字提供有限的動(dòng)態(tài)綁定能力。Objective-C天生即具備鴨子類型之動(dòng)態(tài)綁定能力,因?yàn)檫\(yùn)行期才處理消息,允許發(fā)送未知消息給對象??梢运拖⒔o整個(gè)對象集合而不需要一一檢查每個(gè)對象的類型,也具備消息轉(zhuǎn)送機(jī)制。同時(shí)空對象nil接受消息后默認(rèn)為不做事,所以送消息給nil也不用擔(dān)心程序崩潰。

類的定義與實(shí)現(xiàn)Objective-C中強(qiáng)制要求將類的定義(interface)與實(shí)現(xiàn)(implementation)分為兩個(gè)部分。

類的定義文件遵循C語言之慣例以.h為后綴,實(shí)現(xiàn)文件以.m為后綴。

Interface

定義部分,清楚定義了類的名稱、成員變量和方法。 以關(guān)鍵字@interface作為開始,@end作為結(jié)束。

@interface MyObject : NSObject { int memberVar1; // 實(shí)體變量 id memberVar2;}+(return_type) class_method; // 類方法 -(return_type) instance_method1; // 實(shí)例方法 -(return_type) instance_method2: (int) p1;- (return_type) instance_method3: (int) p1 andPar: (int) p2; @end方法前面的 +/- 號代表函數(shù)的類型:加號(+)代表類方法(class method),不需要實(shí)例就可以調(diào)用,與C++ 的靜態(tài)函數(shù)(static member function)相似。減號(-)即是一般的實(shí)例方法(instance method)。

這里提供了一份意義相近的C++語法對照,如下:

class MyObject : public NSObject {protected: int memberVar1; // 實(shí)體變量 void * memberVar2; public: static return_type class_method(); // 類方法 return_type instance_method1(); // 實(shí)例方法 return_type instance_method2( int p1 ); return_type instance_method3( int p1, int p2 );}Objective-C定義一個(gè)新的方法時(shí),名稱內(nèi)的冒號(:)代表參數(shù)傳遞,不同于C語言以數(shù)學(xué)函數(shù)的括號來傳遞參數(shù)。Objective-C方法使得參數(shù)可以夾雜于名稱中間,不必全部附綴于方法名稱的尾端,可以提高程序可讀性。設(shè)定顏色RGB值的方法為例:

  • (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法*/[myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫方法*/這個(gè)方法的簽名是setColorToRed:Green:Blue:。每個(gè)冒號后面都帶著一個(gè)float類別的參數(shù),分別代表紅,綠,藍(lán)三色。

Implementation

實(shí)現(xiàn)區(qū)塊則包含了公開方法的實(shí)現(xiàn),以及定義私有(private)變量及方法。 以關(guān)鍵字@implementation作為區(qū)塊起頭,@end結(jié)尾。

@implementation MyObject { int memberVar3; //私有實(shí)體變數(shù)}+(return_type) class_method { .... //method implementation}-(return_type) instance_method1 { ....}-(return_type) instance_method2: (int) p1 { ....}-(return_type) instance_method3: (int) p1 andPar: (int) p2 { ....}@end值得一提的是不只Interface區(qū)塊可定義實(shí)體變量,Implementation區(qū)塊也可以定義實(shí)體變量,兩者的差別在于訪問權(quán)限的不同,Interface區(qū)塊內(nèi)的實(shí)體變量默認(rèn)權(quán)限為protected,宣告于implementation區(qū)塊的實(shí)體變量則默認(rèn)為private,故在Implementation區(qū)塊定義私有成員更符合面向?qū)ο笾庋b原則,因?yàn)槿绱祟悇e之私有信息就不需曝露于公開interface(.h文件)中。

創(chuàng)建對象

Objective-C創(chuàng)建對象需通過alloc以及init兩個(gè)消息。alloc的作用是分配內(nèi)存,init則是初始化對象。 init與alloc都是定義在NSObject里的方法,父對象收到這兩個(gè)信息并做出正確回應(yīng)后,新對象才創(chuàng)建完畢。以下為范例:

MyObject * my = [[MyObject alloc] init];在Objective-C 2.0里,若創(chuàng)建對象不需要參數(shù),則可直接使用new

MyObject * my = [MyObject new];僅僅是語法上的精簡,效果完全相同。

若要自己定義初始化的過程,可以重寫init方法,來添加額外的工作。(用途類似C++ 的構(gòu)造函數(shù)constructor)

  • (id) init { if ( self=[super init] ) { // 必須調(diào)用父類的init // do something here ... } return self;}協(xié)議(Protocol)協(xié)議是一組沒有實(shí)現(xiàn)的方法列表,任何的類均可采納協(xié)議并具體實(shí)現(xiàn)這組方法。

Objective-C在NeXT時(shí)期曾經(jīng)試圖引入多重繼承的概念,但由于協(xié)議的出現(xiàn)而沒有實(shí)現(xiàn)之。

協(xié)議類似于Java與C#語言中的“接口”。在Objective-C中,有兩種定義協(xié)議的方式:由編譯器保證的“正式協(xié)議”,以及為特定目的設(shè)定的“非正式協(xié)議”。

非正式協(xié)議為一個(gè)可以選擇性實(shí)現(xiàn)的一系列方法列表。非正式協(xié)議雖名為協(xié)議,但實(shí)際上是掛于NSObject上的未實(shí)現(xiàn)分類(Unimplemented Category)的一種稱謂,Objetive-C語言機(jī)制上并沒有非正式協(xié)議這種東西,OSX 10.6版本之后由于引入@optional關(guān)鍵字,使得正式協(xié)議已具備同樣的能力,所以非正式協(xié)議已經(jīng)被廢棄不再使用。

正式協(xié)議類似于Java中的"接口",它是一系列方法的列表,任何類都可以聲明自身實(shí)現(xiàn)了某個(gè)協(xié)議。在Objective-C 2.0之前,一個(gè)類必須實(shí)現(xiàn)它聲明符合的協(xié)議中的所有方法,否則編譯器會(huì)報(bào)告錯(cuò)誤,表明這個(gè)類沒有實(shí)現(xiàn)它聲明符合的協(xié)議中的全部方法。Objective-C 2.0版本允許標(biāo)記協(xié)議中某些方法為可選的(Optional),這樣編譯器就不會(huì)強(qiáng)制實(shí)現(xiàn)這些可選的方法。

協(xié)議經(jīng)常應(yīng)用于Cocoa中的委托及事件觸發(fā)。例如文本框類通常會(huì)包括一個(gè)委托(delegate)對象,該對象可以實(shí)現(xiàn)一個(gè)協(xié)議,該協(xié)議中可能包含一個(gè)實(shí)現(xiàn)文字輸入的自動(dòng)完成方法。若這個(gè)委托對象實(shí)現(xiàn)了這個(gè)方法,那么文本框類就會(huì)在適當(dāng)?shù)臅r(shí)候觸發(fā)自動(dòng)完成事件,并調(diào)用這個(gè)方法用于自動(dòng)完成功能。

Objective-C中協(xié)議的概念與Java中接口的概念并不完全相同,即一個(gè)類可以在不聲明它符合某個(gè)協(xié)議的情況下,實(shí)現(xiàn)這個(gè)協(xié)議所包含的方法,也即實(shí)質(zhì)上符合這個(gè)協(xié)議,而這種差別對外部代碼而言是不可見的。正式協(xié)議的聲明不提供實(shí)現(xiàn),它只是簡單地表明符合該協(xié)議的類實(shí)現(xiàn)了該協(xié)議的方法,保證調(diào)用端可以安全調(diào)用方法。

語法

協(xié)議以關(guān)鍵字@protocol作為區(qū)塊起始,@end結(jié)束,中間為方法列表。

@protocol Locking- (void)lock;- (void)unlock;@end這是一個(gè)協(xié)議的例子,多線程編程中經(jīng)常要確保一份共享資源同時(shí)只有一個(gè)線程可以使用,會(huì)在使用前給該資源掛上鎖 ,以上即為一個(gè)表明有“鎖”的概念的協(xié)議,協(xié)議中有兩個(gè)方法,只有名稱但尚未實(shí)現(xiàn)。

下面的SomeClass宣稱他采納了Locking協(xié)議:

@interface SomeClass : SomeSuperClass @end一旦SomeClass表明他采納了Locking協(xié)議,SomeClass就有義務(wù)實(shí)現(xiàn)Locking協(xié)議中的兩個(gè)方法。

@implementation SomeClass- (void)lock { // 實(shí)現(xiàn)lock方法...}- (void)unlock { // 實(shí)現(xiàn)unlock方法...}@end由于SomeClass已經(jīng)確實(shí)遵從了Locking協(xié)議,故調(diào)用端可以安全的發(fā)送lock或unlock消息給SomeClass實(shí)體變量,不需擔(dān)心他沒有辦法回應(yīng)消息。

插件是另一個(gè)使用抽象定義的例子,可以在不關(guān)心插件的實(shí)現(xiàn)的情況下定義其希望的行為。

動(dòng)態(tài)類型類似于Smalltalk,Objective-C具備動(dòng)態(tài)類型:即消息可以發(fā)送給任何對象實(shí)體,無論該對象實(shí)體的公開接口中有沒有對應(yīng)的方法。對比于C++這種靜態(tài)類型的語言,編譯器會(huì)擋下對(void*)指針調(diào)用方法的行為。但在Objective-C中,你可以對id發(fā)送任何消息(id很像void*,但是被嚴(yán)格限制只能使用在對象上),編譯器僅會(huì)發(fā)出“該對象可能無法回應(yīng)消息”的警告,程序可以通過編譯,而實(shí)際發(fā)生的事則取決于運(yùn)行期該對象的真正形態(tài),若該對象的確可以回應(yīng)消息,則依舊運(yùn)行對應(yīng)的方法。

一個(gè)對象收到消息之后,他有三種處理消息的可能手段,第一是回應(yīng)該消息并運(yùn)行方法,若無法回應(yīng),則可以轉(zhuǎn)發(fā)消息給其他對象,若以上兩者均無,就要處理無法回應(yīng)而拋出的例外。只要進(jìn)行三者之其一,該消息就算完成任務(wù)而被丟棄。若對“nil”(空對象指針)發(fā)送消息,該消息通常會(huì)被忽略,取決于編譯器選項(xiàng)可能會(huì)拋出例外。

雖然Objective-C具備動(dòng)態(tài)類型的能力,但編譯期的靜態(tài)類型檢查依舊可以應(yīng)用到變量上。以下三種聲明在運(yùn)行時(shí)效力是完全相同的,但是三種聲明提供了一個(gè)比一個(gè)更明顯的類型信息,附加的類型信息讓編譯器在編譯時(shí)可以檢查變量類型,并對類型不符的變量提出警告1。

下面三個(gè)方法,差異僅在于參數(shù)的形態(tài):

  • setMyValue:(id) foo;id形態(tài)表示參數(shù)“foo”可以是任何類的實(shí)例。

  • setMyValue:(id ) foo;id表示“foo”可以是任何類的實(shí)例,但必須采納“aProtocol”協(xié)議。

  • setMyValue:(NSNumber*) foo;該聲明表示“foo”必須是“NSNumber”的實(shí)例。

動(dòng)態(tài)類型是一種強(qiáng)大的特性。在缺少泛型的靜態(tài)類型語言(如Java 5以前的版本)中實(shí)現(xiàn)容器類時(shí),程序員需要寫一種針對通用類型對象的容器類,然后在通用類型和實(shí)際類型中不停的強(qiáng)制類型轉(zhuǎn)換。無論如何,類型轉(zhuǎn)換會(huì)破壞靜態(tài)類型,例如寫入一個(gè)“整數(shù)”而將其讀取為“字符串”會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。這樣的問題被泛型解決,但容器類需要其內(nèi)容對象的類型一致,而對于動(dòng)態(tài)類型語言則完全沒有這方面的問題。

轉(zhuǎn)發(fā)Objective-C允許對一個(gè)對象發(fā)送消息,不管它是否能夠響應(yīng)之。除了響應(yīng)或丟棄消息以外,對象也可以將消息轉(zhuǎn)發(fā)到可以響應(yīng)該消息的對象。轉(zhuǎn)發(fā)可以用于簡化特定的設(shè)計(jì)模式,例如觀測器模式或代理模式。

Objective-C運(yùn)行時(shí)在Object中定義了一對方法:

轉(zhuǎn)發(fā)方法:

  • (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC- (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems響應(yīng)方法:

  • (retval_t) performv:(SEL) sel :(arglist_t) args; // with GCC- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems希望實(shí)現(xiàn)轉(zhuǎn)發(fā)的對象只需用新的方法覆蓋以上方法來定義其轉(zhuǎn)發(fā)行為。無需重寫響應(yīng)方法performv::,由于該方法只是單純的對響應(yīng)對象發(fā)送消息并傳遞參數(shù)。其中,SEL類型是Objective-C中消息的類型。

類別 (Category)在Objective-C的設(shè)計(jì)中,一個(gè)主要的考慮即為大型代碼框架的維護(hù)。結(jié)構(gòu)化編程的經(jīng)驗(yàn)顯示,改進(jìn)代碼的一種主要方法即為將其分解為更小的片段。Objective-C借用并擴(kuò)展了Smalltalk實(shí)現(xiàn)中的“分類”概念,用以幫助達(dá)到分解代碼的目的。

一個(gè)分類可以將方法的實(shí)現(xiàn)分解進(jìn)一系列分離的文件。程序員可以將一組相關(guān)的方法放進(jìn)一個(gè)分類,使程序更具可讀性。舉例來講,可以在字符串類中增加一個(gè)名為“拼寫檢查”的分類,并將拼寫檢查的相關(guān)代碼放進(jìn)這個(gè)分類中。

進(jìn)一步的,分類中的方法是在運(yùn)行時(shí)被加入類中的,這一特性允許程序員向現(xiàn)存的類中增加方法,而無需持有原有的代碼,或是重新編譯原有的類。例如若系統(tǒng)提供的字符串類的實(shí)現(xiàn)中不包含拼寫檢查的功能,可以增加這樣的功能而無需更改原有的字符串類的代碼。

在運(yùn)行時(shí),分類中的方法與類原有的方法并無區(qū)別,其代碼可以訪問包括私有類成員變量在內(nèi)的所有成員變量。

若分類聲明了與類中原有方法同名的函數(shù),則分類中的方法會(huì)被調(diào)用。因此分類不僅可以增加類的方法,也可以代替原有的方法。這個(gè)特性可以用于修正原有代碼中的錯(cuò)誤,更可以從根本上改變程序中原有類的行為。若兩個(gè)分類中的方法同名,則被調(diào)用的方法是不可預(yù)測的。

其它語言也嘗試了通過不同方法增加這一語言特性。TOM在這方面走的更遠(yuǎn),不僅允許增加方法,更允許增加成員變量。也有其它語言使用面向聲明的解決方案,其中最值得注意的是Self語言。

C#與Visual Basic.NET語言以擴(kuò)展函數(shù)的與不完全類的方式實(shí)現(xiàn)了類似的功能。Ruby與一些動(dòng)態(tài)語言則以"monkey patch"的名字稱呼這種技術(shù)。

使用分類的例子

這個(gè)例子創(chuàng)建了Integer類,其本身只定義了integer屬性,然后增加了兩個(gè)分類Arithmetic與Display以擴(kuò)展類的功能。雖然分類可以訪問類的私有成員,但通常利用屬性的訪問方法來訪問是一種更好的做法,可以使得分類與原有類更加獨(dú)立。這是分類的一種典型應(yīng)用—另外的應(yīng)用是利用分類來替換原有類中的方法,雖然用分類而不是繼承來替換方法不被認(rèn)為是一種好的做法。

語言變化Objective-C++Objective-C++是GCC的一個(gè)前端,它可以編譯混合了C++與Objective-C語法的源文件。Objective-C++是C++的擴(kuò)展,類似于Objective-C是C的擴(kuò)展。由于在融合C++與Objective-C兩種語言的特性方面沒有做特別的工作,因此有以下限制:

C++類不能從Objective-C類繼承,反之亦然。

Objective-C定義內(nèi)部不能定義C++名字空間。

Objective-C類的成員變量不能包括不含默認(rèn)構(gòu)造函數(shù)和/或含有虛方法的C++類對象,但使用C++類指針并無如此限制(可以在 -init方法中對之進(jìn)行初始化)。

C++“傳遞值”的特性不能用在Objective-C對象上,而只能傳遞其指針。

Objective-C聲明不能存在在C++模板聲明中,反之亦然。但Objective-C類型可以用在C++模板的參數(shù)中。

Objective-C和C++的錯(cuò)誤處理語句不同,各自的語句只能處理各自的錯(cuò)誤。

Objective-C錯(cuò)誤使得C++對象被退出時(shí),C++析構(gòu)函數(shù)不會(huì)被調(diào)用。新的64位運(yùn)行時(shí)解決了這個(gè)問題。

Objective-C 2.0在2006年7月蘋果全球開發(fā)者會(huì)議中,Apple宣布了“Objective-C 2.0”的發(fā)布,其增加了“現(xiàn)代的垃圾收集,語法改進(jìn),運(yùn)行時(shí)性能改進(jìn),以及64位支持”。2007年10月發(fā)布的Mac OS X v10.5中包含了Objective-C 2.0的編譯器。

垃圾收集

Objective-C 2.0提供了一個(gè)可選的垃圾收集器。在向后兼容模式中,Objective-C運(yùn)行時(shí)會(huì)將引用計(jì)數(shù)操作,例如“retain”與“release”變?yōu)闊o操作。當(dāng)垃圾收集啟用時(shí),所有的對象都是收集器的工作對象。普通的C指針可以以“__strong”修飾,標(biāo)記指針指向的對象仍在使用中。被標(biāo)記為“__weak”的指針不被計(jì)入收集器的計(jì)數(shù)中,并在對象被回收時(shí)改寫為“nil”。iOS上的Objective-C 2.0實(shí)現(xiàn)中不包含垃圾收集器。垃圾收集器運(yùn)行在一個(gè)低優(yōu)先級的后臺(tái)線程中,并可以在用戶動(dòng)作時(shí)暫停,從而保持良好的用戶體驗(yàn)。

屬性

Objective-C 2.0引入了新的語法以聲明變量為屬性,并包含一可選定義以配置訪問方法的生成。屬性總是為公共的,其目的為提供外部類訪問(也可能為只讀)類的內(nèi)部變量的方法。屬性可以被聲明為“readonly”,即只讀的,也可以提供儲(chǔ)存方法包括“assign”,“copy”或“retain”(簡單的賦值、復(fù)制或增加1引用計(jì)數(shù))。默認(rèn)的屬性是原子的,即在訪問時(shí)會(huì)加鎖以避免多線程同時(shí)訪問同一對象,也可以將屬性聲明為“nonatomic”(非原子的),避免產(chǎn)生鎖。

@interface Person : NSObject { @public NSString *name; @private int age;}@property(copy) NSString *name;@property(readonly) int age;-(id)initWithAge:(int)age;@endModern Objective-C蘋果公司在 WWDC2012 大會(huì)上介紹了大量 Objective-C 的新特性,能夠讓程序員更加高效地編寫代碼,這些新特性在 Xcode4.4 版本中已經(jīng)可以使用。

Object Literals

默認(rèn)生成 @synthesize 代碼

遍歷元素

Subscripting Methods

語言分析Objective-C是非?!皩?shí)際”的語言。它用一個(gè)很小的、用C寫成的運(yùn)行庫,使得應(yīng)用程序的大小增加很少,與此相比,大部分OO系統(tǒng)需要極大的運(yùn)行時(shí)虛擬機(jī)來執(zhí)行。ObjC寫成的程序通常不會(huì)比其源代碼和庫(通常無需包含在軟件發(fā)行版本中)大太多,不會(huì)像Smalltalk系統(tǒng),即使只是打開一個(gè)窗口也需要大量的容量。由于Obj-C的動(dòng)態(tài)類型特征,Obj-C不能對方法進(jìn)行內(nèi)聯(lián)(inline)一類的優(yōu)化,使得Obj-C的應(yīng)用程序一般比類似的C或C++程序更大。

Obj-C可以在現(xiàn)存C編譯器基礎(chǔ)上實(shí)現(xiàn)(在GCC中,Obj-C最初作為預(yù)處理器引入,后來作為模塊存在),而不需要編寫一個(gè)全新的編譯器。這個(gè)特性使得Obj-C能利用大量現(xiàn)存的C代碼、庫、工具和編程思想等資源。現(xiàn)存C庫可以用Obj-C包裝器來提供一個(gè)Obj-C使用的OO風(fēng)格界面包裝。

以上這些特性極大地降低了進(jìn)入Obj-C的門檻,這是1980年代Smalltalk在推廣中遇到的最大問題。

爭議Objective-C的最初版本并不支持垃圾回收(garbage collection)。在當(dāng)時(shí)這是爭論的焦點(diǎn)之一,很多人考慮到Smalltalk回收時(shí)有漫長的“死亡時(shí)間”,令整個(gè)系統(tǒng)失去功用,Objective-C為避免此問題才不擁有這個(gè)功能。某些第三方版本加入了這個(gè)功能(尤是GNUstep),蘋果公司也在其Mac OS X 10.5中提供了實(shí)現(xiàn)。

另一個(gè)廣受批評的問題是ObjC不包括名字空間機(jī)制(namespace mechanism)。取而代之的是程序員必須在其類別名稱加上前綴,由于前綴往往較短(相比名字空間),這時(shí)常引致沖突。在2007年,在Cocoa編程環(huán)境中,所有Mac OS X類別和函數(shù)均有“NS”作為前綴,例如NSObject或NSButton來清楚分辨它們屬于Mac OS X核心;使用“NS”是由于這些類別的名稱在NeXTSTEP開發(fā)時(shí)定下。

雖然Objective-C是C的嚴(yán)格超集,但它也不視C的基本類型為第一級的對象。

和C++不同,Objective-C不支持運(yùn)算符重載(它不支持ad-hoc多態(tài))。亦與C++不同,但和Java相同,Objective-C只容許對象繼承一個(gè)類別(不設(shè)多重繼承)。Categories和protocols不但可以提供很多多重繼承的好處,而且沒有很多缺點(diǎn),例如額外運(yùn)行時(shí)間過重和二進(jìn)制不兼容。

由于Obj-C使用動(dòng)態(tài)運(yùn)行時(shí)類型,而且所有的方法都是函數(shù)調(diào)用(有時(shí)甚至連系統(tǒng)調(diào)用(syscalls)也如此),很多常見的編譯時(shí)性能優(yōu)化方法都不能應(yīng)用于Obj-C(例如:內(nèi)聯(lián)函數(shù)、常數(shù)傳播、交互式優(yōu)化、純量取代與聚集等)。這使得Obj-C性能劣于類似的對象抽象語言(如C++)。不過Obj-C擁護(hù)者認(rèn)為Obj-C本就不應(yīng)應(yīng)用于C++或Java常見的底層抽象,Obj-C的應(yīng)用方向是對性能要求不大的應(yīng)用

優(yōu)點(diǎn)及缺點(diǎn)Objective-C是非?!皩?shí)際”的語言。它使用一個(gè)用C寫成、很小的運(yùn)行庫,只會(huì)令應(yīng)用程序的大小增加很小,和大部分OO系統(tǒng)使用極大的VM執(zhí)行時(shí)間會(huì)取代了整個(gè)系統(tǒng)的運(yùn)作相反,ObjC寫成的程序通常不會(huì)比其原始碼大很多。而其庫函數(shù)(通常沒附在軟件發(fā)行本)亦和Smalltalk系統(tǒng)要使用極大的內(nèi)存來開啟一個(gè)窗口的情況相反。 Objective-C的最初版本并不支持垃圾回收。在當(dāng)時(shí)這是爭論的焦點(diǎn)之一,很多人考慮到Smalltalk回收時(shí)有漫長的“死亡時(shí)間”,令整個(gè)系統(tǒng)失去作用。Objective-C為避免此問題才不擁有這個(gè)功能。雖然某些第三方版本已加入這個(gè)功能(尤是GNUstep),Apple在其Mac OS X 10.3中仍未引入這個(gè)功能。 另一個(gè)問題是ObjC不包括命名空間機(jī)制(namespace mechanism)。取而代之的是程序設(shè)計(jì)師必須在其類別名稱加上前綴,時(shí)常引致沖突。在2004年,在Cocoa編程環(huán)境中,所有Mac OS X類別和函數(shù)均有“NS”作為前綴,例如NSObject或NSButton來清楚分辨它們屬于Mac OS X核心;使用“NS”是由于這些類別的名稱在NeXTSTEP開發(fā)時(shí)定下。 雖然Objective-C是C的母集,但它也不視C的基本型別為第一級的對象。 和C++不同,Objective-C不支持運(yùn)算子重載(它不支持ad-hoc多型)。亦與C++不同,但和Java相同,Objective-C只容許對象繼承一個(gè)類(不設(shè)多重繼承)。Categories和protocols不但可以提供很多多重繼承的好處,而且沒有很多缺點(diǎn),例如額外執(zhí)行時(shí)間過重和二進(jìn)制不兼容。

和C++的比較目前好象只有Apple使用Objective-C作為其支持的語言吧。2

與C++的不同之處有:

OC中所有的類都必須繼承自NSObject。

OC中所有對象都是指針的形式。

OC用self代替this。

OC使用id代替void*。

OC使用nil表示NULL

OC只支持單繼承。

OC使用YES/NO表示TRUE/FALSE

OC使用#import代替#include

OC中用消息表示類的方法,并采用[aInstance method:argv]調(diào)用形式。

OC支持反射機(jī)制

OC支持Dynamic Typing, Dynamic Binding和Dynamic Loading。

與C++的相同之處有:

與C共享的部分一致。

可以使用assert(BOOL), 一般用NSCParameterAssert(BOOL)代替。

OC中的命名前綴說明:

NS-:NextStep

CF-:Core Foundation

CA-:Core Animation

CG-:Core Graphics

UI-:User Interface

OC中的消息特殊性:

調(diào)用消息的類可以不知道如何響應(yīng)這個(gè)消息。如果它不知道如何處理這個(gè)消息,它會(huì)自動(dòng)的將這個(gè)消息轉(zhuǎn)給其他的類,比如它的父類。

調(diào)用消息的類可以是nil。在C++中,在使用類方法之前,我們都需要檢查對象是否為空,所以在實(shí)現(xiàn)析構(gòu)函數(shù)的時(shí)候,常會(huì)有如下的代碼,如if (var) { delete var; }但是在objective c中,我們就可以直接寫[var release];即使var == nil, 也不會(huì)有問題。

OC中的函數(shù)聲明格式有:

-/+ (return type) function_name;

-/+ (return type) function_name : (parameter type) parameter;

-/+ (return type) function_name : (parameter type) parameter1otherParameter: (parameter_type) parameter2

以上參數(shù)說明:-表示一般函數(shù),+表示靜態(tài)函數(shù)。otherParameter是參數(shù)的別名(第一個(gè)參數(shù)的別名省略),在函數(shù)調(diào)用時(shí)方便指定。

OC中的構(gòu)造/析構(gòu)函數(shù)

OC中的init()/release()對應(yīng)于C++的構(gòu)造/析構(gòu)函數(shù)。alloc()/dealloc()也就對應(yīng)于C++的new和delete,其中的dealloc()由于引用計(jì)數(shù)的自動(dòng)調(diào)用而不用手動(dòng)調(diào)用。

OC中父類的init()/release()函數(shù)需要子類的手動(dòng)調(diào)用。而且每次都必須調(diào)用。不同于C++的自動(dòng)調(diào)用。

構(gòu)造函數(shù)(- (id) init)調(diào)用形如:CSample* pSample=[CSample alloc] init];其中alloc(+ (id) alloc)是繼承來的static函數(shù),init是繼承來的一般函數(shù),如重寫一般函數(shù)時(shí),則相當(dāng)于C++的覆蓋(不帶參數(shù))或重載(帶參數(shù))。

析構(gòu)函數(shù)(- (void) release)將引用計(jì)數(shù)減1,當(dāng)=0時(shí)父類的release()會(huì)自動(dòng)調(diào)用dealloc(- (void) dealloc);

當(dāng)OC沒有數(shù)據(jù)成員時(shí),可省略{},建議保留。

繼承下來的方法,如:-(id) init可以頭文件中省略,建議保留

OC中只有數(shù)據(jù)成員的訪問限制,沒有方法的訪問限制。

同C++一樣,數(shù)據(jù)成員有三種訪問限制public, protected, private,缺省是protected。

示例:@interface AccessExample: NSObject {
@public
int publicVar;
@protected
int protectedVar;
@private
int privateVar;
}
@end

方法的訪問限制可通過Category實(shí)現(xiàn)

示例:

@interface MyClass

- (void) sayHello {

NSLog(@"Hello");

}

@end

@interface MyClass(Private)

- (void) kissGoodbye;

@end

OC中沒有類的靜態(tài)變量,只有全局變量

OC中的數(shù)組NSArray可以保存不同對象類型的數(shù)據(jù)。

OC也支持runtime時(shí)的類型檢查

- (BOOL) isKindOfClass: classObj
用于判斷該對象是否屬于某個(gè)類或者它的子類

- (BOOL) isMemberOfClass: classObj
用于判斷該對象是否屬于某個(gè)類(這里不包括子類)

- (BOOL) respondsToSelector: selector
用于判斷該對象是否能響應(yīng)某個(gè)消息。這里,我們可以將@selector后面帶的參數(shù)理解為C++中的函數(shù)指針。
注意:1)不要忘了@ ;2)@selector后面用的是(),而不是[ ] ;3)要在消息名稱后面跟:,無論這個(gè)消息是否帶參數(shù)。如:[pSquare respondsToSelector:@selector(Set: andHeight:)]。

+ (BOOL) instancesRespondToSelector: selector
用于判斷該類是否能響應(yīng)某個(gè)消息。這是一個(gè)靜態(tài)函數(shù)。

-(id) performSelector: selector :調(diào)用對象的selector方法。

conformsToProtocol 類似于respondsToSelector ,用于動(dòng)態(tài)檢查某個(gè)對象是否遵守某個(gè)協(xié)議。

Category:在沒有源代碼的情況下,為一個(gè)已經(jīng)存在的類添加一些新的功能

只能添加新的方法,不能添加新的數(shù)據(jù)成員

Category的名字必須是唯一的

Protocol:相當(dāng)于C++中的純虛類

形如:@interface MyDate: NSObject { } @end

使用:MyDate * dat = [[MyDate alloc] init];id var = dat; [var print]。

說明:我們首先聲明了Printing 協(xié)議,任何遵守這個(gè)協(xié)議的類,都必須實(shí)現(xiàn)print 方法。在Objective C 中,我們通過來表示遵守某個(gè)協(xié)議。當(dāng)某個(gè)類聲明要遵守某個(gè)協(xié)議之后,它就必須在.m文件中實(shí)現(xiàn)這個(gè)協(xié)議中的所有方法。使用id 作為類型,而不是象C++中的Printing* var。

IBOutlet, IBAction: 這兩個(gè)東西其實(shí)在語法中沒有太大的作用。如果你希望在Interface Builder中能看到這個(gè)控件對象,那么在定義的時(shí)候前面加上IBOutlet,在IB里就能看到這個(gè)對象的outlet,如果你希望在Interface Builder里控制某個(gè)對象執(zhí)行某些動(dòng)作,就在方法前面加上(IBAction)。

盡量避免在一行語句中進(jìn)行兩層以上的嵌套

消息轉(zhuǎn)發(fā):- (void) forwardInvocation: (NSInvocation*)anInvocation;

擴(kuò)展的關(guān)鍵字@interface類型聲明,類似于c++中的class,區(qū)別在于Object c中的聲明與實(shí)現(xiàn)是強(qiáng)制分開的,@interface關(guān)鍵字用于類型的聲明,包括數(shù)據(jù)成員、方法聲明、屬性等。方法的參數(shù)傳遞采用中綴符的形式,利用“:”分割參數(shù)名和被傳遞參數(shù),類型的聲明以@interface開頭,以@end結(jié)束,通常一個(gè)類型的聲明采用下面的結(jié)構(gòu):

@class someOtherObject //外部類型聲明

@interfacesomeObject:NSObject //繼承的類型

{

int i; //成員變量

}

-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg; //對象的方法

+(id)someMethod:(int)someArg; //類方法

-(id)init; //初始化方法

@propertyint num; //屬性

@end

@implementation對應(yīng)于@interface的類型聲明,@implementation表示一個(gè)類型的實(shí)現(xiàn)過程,同樣以@end結(jié)束,實(shí)現(xiàn)的格式通常如下:

@implementationsomeObject

-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg

{

//實(shí)現(xiàn)代碼

}

@synthesize num=i; //將屬性與變量進(jìn)行對應(yīng)

@end

new、allocObject C中的方法調(diào)用形式采用消息發(fā)送的方式,通常調(diào)用的形式如

[someObject someMethod:firstArg someOtherArgName:otherArg]

實(shí)例的初始化也采用消息發(fā)送的形式,可以簡單的調(diào)用類型的new方法來獲取一個(gè)實(shí)例對象,簡單實(shí)例化的方法通常是:

someObject *obj = [someObjectnew]; //類的實(shí)例化

new方法的實(shí)際過程是調(diào)用allocinit方法,因此如果需要采用自定義的方法來初始化實(shí)例,則需要自己重寫init方法,通常的初始化方式為:

someObject *obj = [[someObject alloc] init]; //采用無參數(shù)的init實(shí)例化

someObject *obj = [[someObject alloc] initWithArg:Arg]; //采用參數(shù)的實(shí)例化

@class**@class是一個(gè)前向引用聲明,類似于C++中的friend友元聲明,其作用是告訴編譯器其后面的字段代表一個(gè)類型名稱,盡管不知道類型的具體實(shí)現(xiàn),但是只需要將其作為一個(gè)類型名稱處理即可。通常在使用復(fù)合的結(jié)構(gòu)時(shí)可以采用@class來減少頭文件的相互引用,如果出現(xiàn)循環(huán)依賴,則需要依靠@class**來避免引用的死循環(huán)。通常使用形式為:

@classsomeOtherObject;

@interfacesomeObject:NSObject

{

someOtherObject *obj;

}

@end

@property盡管可以使用obj->arr的形式去強(qiáng)制讀取對象的成員變量,但是良好的編程形式是對外界提供成員變量的讀寫接口。@property關(guān)鍵字提供了外界對成員變量的訪問接口,其本質(zhì)是為某一個(gè)屬性提供set和get操作。根據(jù)不同的需要,可以添加readonly(只讀,相當(dāng)于只添加get不添加set方法)或者readwrite(讀寫,如果不添加則為默認(rèn));還有三種賦值方式可選3:assign(直接賦值,通常用于基本類型),retain(釋放舊值,增加新的retaincount),copy(常用于字符串,生成一個(gè)新的拷貝)。通常使用的方式如下:

@interface someObject:NSObject

{

int i; //成員變量

}

@property (assign,readonly) int num; //屬性

@end

@synthesize與**@property**對應(yīng),將一個(gè)外在屬性與成員變量相關(guān)聯(lián),定義在@implementation中,如果屬性名與變量名一致則可以省略變量名。常用方法:

@implementation someObject

@synthesize num=i;//如果屬性名也為i,則可以直接寫為 @synthesizei

@end

內(nèi)存管理Object C采用引用計(jì)數(shù)的方式進(jìn)行內(nèi)存管理,由于所有的對象都繼承于NSObject,因此所有的對象都可以接受NSObject的三個(gè)方法:

-(id)retain;

-(void)release;

-(unsigned)retainCount;

retain方法將對象的引用計(jì)數(shù)加一并返回該對象,release將引用計(jì)數(shù)減一,retainCount方法返回對象當(dāng)前的引用計(jì)數(shù)。當(dāng)采用new、alloc、copy方法創(chuàng)建一個(gè)對象時(shí),它的引用計(jì)數(shù)被置為1,如果程序中對該對象進(jìn)行操作,則應(yīng)根據(jù)需要,通過調(diào)用retain和release方法來保證該對象在不需要的時(shí)候被清除。當(dāng)一個(gè)對象的引用計(jì)數(shù)被置為0后,系統(tǒng)會(huì)自動(dòng)向?qū)ο蟀l(fā)送一個(gè)dealloc消息,將其占有的資源釋放。通常情況下,如果一個(gè)對象的初始化過程調(diào)用了其他資源,則應(yīng)該重寫改對象的dealloc過程,保證在對象的銷毀期正確釋放這些資源。

為了更加方便的進(jìn)行能存管理,cocoa中提供了一個(gè)自動(dòng)釋放池(autorelease pool)的概念,每一個(gè)類都繼承了一個(gè)autorelease方法,當(dāng)調(diào)用對象的autorelease方法時(shí),改對象會(huì)被加入到開始創(chuàng)建的自動(dòng)釋放池中。當(dāng)程序進(jìn)行到不再需要自動(dòng)釋放池中的對象時(shí),將自動(dòng)釋放池釋放的時(shí)候會(huì)向池中的所有對象發(fā)送一個(gè)release消息,從而保證不再需要的對象被正確的釋放。通常的用法如下:

NSAutoreleasePool *pool;

pool = [[NSAutoreleasePool alloc] init];

someObject * obj = [[someObject alloc] init];

[obj autorelease]; //加入自動(dòng)釋放池

//其他代碼

[pool release]; //執(zhí)行該語句時(shí),系統(tǒng)會(huì)向池內(nèi)所有的對象發(fā)送release消息;在這個(gè)例子中,如果對obj進(jìn)行的其他retain操作和release操作保持一致的話,則會(huì)將obj的引用計(jì)數(shù)變?yōu)?,從而調(diào)用它的dealloc方法進(jìn)行資源釋放

Object C進(jìn)行內(nèi)存管理的3條規(guī)則是:

如果使用new、alloc或copy操作獲得一個(gè)對象,則該對象的保留計(jì)數(shù)器值為1

如果通過任何其他方法獲得一個(gè)對象,則假設(shè)該對象的保留計(jì)數(shù)器值為1,而且已經(jīng)被設(shè)置為自動(dòng)釋放

如果保留了某個(gè)對象,則必須保持retain方法和release方法的使用次數(shù)相等。

類別類別是為現(xiàn)有的類提供一個(gè)新的方法的方法,即使沒有一個(gè)類的源代碼,仍然可以向類中添加一個(gè)方法以方便使用。類別的主要目的有3個(gè):將類的實(shí)現(xiàn)分散到多個(gè)不同的文件或框架中,創(chuàng)建對私有方法的前向引用,向?qū)ο筇砑臃钦絽f(xié)議。

類別的聲明方法:

@interfacesomeObject (someProtocal)

-(void)someMethod;

@end

類別的實(shí)現(xiàn)方法:

@implementationsomeObject(someProtocal)

-(void)someMethod

{

}

@end

@protocolObject C中的協(xié)議類似于java中的接口,通過**@protocol**關(guān)鍵字定義一個(gè)或多個(gè)需要遵從協(xié)議的對象實(shí)現(xiàn)的方法,協(xié)議定義的方法:

@protocolsomeProtocol

-(void)someMethod;

@end

采用協(xié)議的方法是在類聲明時(shí)使用尖括號注明其需要使用的協(xié)議:

@interfacesomeObject:NSObject

在類實(shí)現(xiàn)時(shí)需要將協(xié)議中規(guī)定的方法都予以實(shí)現(xiàn)。

Object C 2.0增加了2個(gè)新的協(xié)議修飾符**@optional@required**,可以規(guī)定協(xié)議中的方法是否為必須實(shí)現(xiàn)的方法。

本詞條內(nèi)容貢獻(xiàn)者為:

閆曉東 - 副教授 - 中央民族大學(xué)信息工程學(xué)院