Objective-C[編輯]
維基百科,自由的百科全書
Objective-C
編程范型
面向?qū)ο?/a>
實現(xiàn)者
蘋果公司
發(fā)行時間
1983
最新發(fā)行時間
Objective-C 2.1[1]
(2007年10月26日)
類型系統(tǒng)
靜態(tài)類型腹缩、動態(tài)類型翔横、弱類型
主要實現(xiàn)產(chǎn)品
啟發(fā)語言
影響語言
Java、Objective-J曼尊、TOM盟榴、Nu語言曹质、Swift
Objective-C是一種通用、高級曹货、面向?qū)ο?/a>的編程語言咆繁。它擴展了標準的ANSI C編程語言,將Smalltalk式的消息傳遞機制加入到ANSI C中顶籽。目前主要支持的編譯器有GCC和Clang(采用LLVM作為前端)玩般。
Objective-C的商標權(quán)屬于蘋果公司,蘋果公司也是這個編程語言的主要開發(fā)者礼饱。蘋果在開發(fā)NeXTSTEP操作系統(tǒng)時使用了Objective-C坏为,之后被OS X和iOS繼承下來。現(xiàn)在Objective-C與Swift是OS X和iOS操作系統(tǒng)镊绪、及與其相關(guān)的API匀伏、Cocoa和Cocoa Touch的主要編程語言。
目錄
[隱藏]
1歷史
2語法2.1Hello World
2.2消息傳遞
2.3類的定義與實現(xiàn)2.3.1Interface
2.3.2Implementation
2.3.3創(chuàng)建對象
2.4協(xié)議(Protocol)
2.5動態(tài)類型
2.6轉(zhuǎn)發(fā)2.6.1例子
2.6.2腳注
2.7類別 (Category)2.7.1使用分類的例子
2.7.2注釋
3語言變化3.1Objective-C++
3.2Objective-C 2.03.2.1垃圾收集
3.2.2屬性
3.2.3快速枚舉
歷史[編輯]
Objective-C 主要由 Stepstone 公司的布萊德·考克斯(Brad Cox)和 Tom Love 在 1980 年代發(fā)明蝴韭。
1981年 Brad Cox 和 Tom Love 還在 ITT 公司技術(shù)中心任職時够颠,接觸到了 SmallTalk語言。Cox 當時對軟件設計和開發(fā)問題非常感興趣榄鉴,他很快地意識到 SmallTalk語言 在系統(tǒng)工程構(gòu)建中具有無法估量的價值履磨,但同時他和 Tom Love 也明白,目前 ITT 公司的電子通信工程相關(guān)技術(shù)中庆尘,C 語言被放在很重要的位置剃诅。
于是 Cox 撰寫了一個 C 語言的預處理器,打算使 C 語言具備些許 Smalltalk 的本領(lǐng)驶忌。Cox 很快地實現(xiàn)了一個可用的 C 語言擴展矛辕,此即為 Objective-C語言的前身。到了 1983 年付魔,Cox 與 Love 合伙成立了 Productivity Products International(PPI)公司聊品,將 Objective-C 及其相關(guān)庫商品化販售,并在之后將公司改名為StepStone几苍。1986年杨刨,Cox 出版了一本關(guān)于 Objective-C 的重要著作《Object-Oriented Programming, An Evolutionary Approach》,書內(nèi)詳述了 Objective-C 的種種設計理念擦剑。
1988年妖胀,斯蒂夫·喬布斯(Steve Jobs)離開蘋果公司后成立了 NeXT Computer 公司芥颈,NeXT 公司買下 Objective-C 語言的授權(quán),并擴展了著名的開源編譯器GCC 使之支持 Objective-C 的編譯赚抡,基于 Objective-C 開發(fā)了 AppKit 與 Foundation Kit 等庫爬坑,作為 NeXTSTEP 的的用戶界面與開發(fā)環(huán)境的基礎(chǔ)。雖然 NeXT 工作站后來在市場上失敗了涂臣,但 NeXT 上的軟件工具卻在業(yè)界中被廣泛贊揚盾计。這促使 NeXT 公司放棄硬件業(yè)務,轉(zhuǎn)型為銷售NeXTStep(以及OpenStep)平臺為主的軟件公司赁遗。
1992年署辉,自由軟件基金會的 GNU 開發(fā)環(huán)境增加了對 Objective-C 的支持。1994年岩四,NeXT Computer公司和Sun Microsystem聯(lián)合發(fā)布了一個針對 NEXTSTEP 系統(tǒng)的標準典范哭尝,名為 OPENSTEP。OPENSTEP 在自由軟件基金會的實現(xiàn)名稱為 GNUstep剖煌。1996年12月20日材鹦,蘋果公司宣布收購 NeXT Software 公司,NEXTSTEP/OPENSTEP環(huán)境成為蘋果操作系統(tǒng)下一個主要發(fā)行版本OS X的基礎(chǔ)耕姊。這個開發(fā)環(huán)境的版本被蘋果公司稱為Cocoa桶唐。
2005年,蘋果電腦雇用了克里斯·拉特納及LLVM開發(fā)團隊[2]
茉兰,clang及LLVM成為蘋果公司在GCC之外的新編譯器選擇尤泽,在 Xcode 4.0之后均采用 LLVM 作為默認的編譯器。最新的 Objective-C 特性也都率先在 Clang 上實現(xiàn)规脸。
語法[編輯]
Objective-C是C語言的嚴格超集--任何C語言程序不經(jīng)修改就可以直接通過Objective-C編譯器坯约,在Objective-C中使用C語言代碼也是完全合法的。Objective-C被描述為蓋在C語言上的薄薄一層燃辖,因為Objective-C的原意就是在C語言主體上加入面向?qū)ο?/a>的特性鬼店。Objective-C的面向?qū)ο?/a>語法源于Smalltalk消息傳遞風格网棍。所有其他非面向?qū)ο?/a>的語法黔龟,包括變量類型,預處理器(preprocessing)滥玷,流程控制氏身,函數(shù)聲明與調(diào)用皆與C語言完全一致。但有些C語言語法合法代碼在objective-c中表達的意思不一定相同惑畴,比如某些布爾表達式蛋欣,在C語言中返回值為true,但在Objective-C若與yes直接相比較如贷,函數(shù)將會出錯陷虎,因為在Objective-C中yes的值只表示為1.
Hello World[編輯]
這里示范了一個基礎(chǔ)的Hello World程序到踏。基于Xcode 4.3.1:
import <Foundation/Foundation.h>int main(int argc, char *argv[]) { @autoreleasepool { NSLog(@"Hello World!"); } return 0;}
消息傳遞[編輯]
Objective-C最大的特色是承自Smalltalk的消息傳遞模型(message passing)尚猿,此機制與今日C++式之主流風格差異甚大窝稿。Objective-C里,與其說對象互相調(diào)用方法凿掂,不如說對象之間互相傳遞消息更為精確伴榔。此二種風格的主要差異在于調(diào)用方法/消息傳遞這個動作。C++里類別與方法的關(guān)系嚴格清楚庄萎,一個方法必定屬于一個類別踪少,而且在編譯時(compile time)就已經(jīng)緊密綁定,不可能調(diào)用一個不存在類別里的方法糠涛。但在Objective-C援奢,類別與消息的關(guān)系比較松散,調(diào)用方法視為對對象發(fā)送消息脱羡,所有方法都被視為對消息的回應萝究。所有消息處理直到運行時(runtime)才會動態(tài)決定,并交由類別自行決定如何處理收到的消息锉罐。也就是說帆竹,一個類別不保證一定會回應收到的消息,如果類別收到了一個無法處理的消息脓规,程序只會拋出異常栽连,不會出錯或崩潰。
C++里侨舆,送一個消息給對象(或者說調(diào)用一個方法)的語法如下:
obj.method(argument);
Objective-C則寫成:
[obj method: argument];
此二者并不僅僅是語法上的差異秒紧,還有基本行為上的不同。
這里以一個汽車類(car class)的簡單例子來解釋Objective-C的消息傳遞特性:
[car fly];
典型的C++意義解讀是“調(diào)用car類別的fly方法”挨下。若car類別里頭沒有定義fly方法熔恢,那編譯肯定不會通過。但是Objective-C里臭笆,我們應當解讀為“發(fā)提交一個fly的消息給car對象”叙淌,fly是消息,而car是消息的接收者愁铺。car收到消息后會決定如何回應這個消息鹰霍,若car類別內(nèi)定義有fly方法就運行方法內(nèi)之代碼,若car內(nèi)不存在fly方法茵乱,則程序依舊可以通過編譯茂洒,運行期則拋出異常。
此二種風格各有優(yōu)劣瓶竭。C++強制要求所有的方法都必須有對應的動作督勺,且編譯期綁定使得函數(shù)調(diào)用非城撸快速。缺點是僅能借由virtual關(guān)鍵字提供有限的動態(tài)綁定能力智哀。Objective-C天生即具備鴨子類型之動態(tài)綁定能力堵未,因為運行期才處理消息,允許發(fā)送未知消息給對象盏触∩罚可以送消息給整個對象集合而不需要一一檢查每個對象的類型,也具備消息轉(zhuǎn)送機制赞辩。同時空對象nil接受消息后默認為不做事雌芽,所以送消息給nil也不用擔心程序崩潰。
類的定義與實現(xiàn)[編輯]
Objective-C中強制要求將類的定義(interface)與實現(xiàn)(implementation)分為兩個部分辨嗽。
類的定義文件遵循C語言之慣例以.h為后綴世落,實現(xiàn)文件以.m為后綴。
Interface[編輯]
定義部分糟需,清楚定義了類的名稱屉佳、數(shù)據(jù)成員和方法。 以關(guān)鍵字@interface作為開始洲押,@end作為結(jié)束武花。
@interface MyObject : NSObject { int memberVar1; // 實體變量 id memberVar2;}+(return_type) class_method; // 類方法-(return_type) instance_method1; // 實例方法-(return_type) instance_method2: (int) p1;-(return_type) instance_method3: (int) p1 andPar: (int) p2;@end
方法前面的 +/- 號代表函數(shù)的類型:加號(+)代表類方法(class method),不需要實例就可以調(diào)用杈帐,與C++ 的靜態(tài)函數(shù)(static member function)相似体箕。減號(-)即是一般的實例方法(instance method)。
這里提供了一份意義相近的C++語法對照挑童,如下:
class MyObject : public NSObject {protected: int memberVar1; // 實體變量 void * memberVar2; public: static return_type class_method(); // 類方法 return_type instance_method1(); // 實例方法 return_type instance_method2( int p1 ); return_type instance_method3( int p1, int p2 );}
Objective-C定義一個新的方法時累铅,名稱內(nèi)的冒號(:)代表參數(shù)傳遞,不同于C語言以數(shù)學函數(shù)的括號來傳遞參數(shù)站叼。Objective-C方法使得參數(shù)可以夾雜于名稱中間娃兽,不必全部附綴于方法名稱的尾端,可以提高程序可讀性尽楔。設定顏色RGB值的方法為例:
- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法/[myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; / 呼叫方法*/
這個方法的簽名是setColorToRed:Green:Blue:投储。每個冒號后面都帶著一個float類別的參數(shù),分別代表紅翔试,綠轻要,藍三色复旬。
Implementation[編輯]
實現(xiàn)區(qū)塊則包含了公開方法的實現(xiàn)垦缅,以及定義私有(private)變量及方法。 以關(guān)鍵字@implementation作為區(qū)塊起頭驹碍,@end結(jié)尾壁涎。
@implementation MyObject { int memberVar3; //私有實體變數(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ū)塊可定義實體變量凡恍,Implementation區(qū)塊也可以定義實體變量,兩者的差別在于訪問權(quán)限的不同怔球,Interface區(qū)塊內(nèi)的實體變量默認權(quán)限為protected嚼酝,宣告于implementation區(qū)塊的實體變量則默認為private,故在Implementation區(qū)塊定義私有成員更匹配面向?qū)ο笾庋b原則竟坛,因為如此類別之私有信息就不需曝露于公開interface(.h文件)中闽巩。
創(chuàng)建對象[編輯]
Objective-C創(chuàng)建對象需通過alloc以及init兩個消息。alloc的作用是分配內(nèi)存担汤,init則是初始化對象涎跨。 init與alloc都是定義在NSObject里的方法,父對象收到這兩個信息并做出正確回應后崭歧,新對象才創(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é)議是一組沒有實現(xiàn)的方法列表仔粥,任何的類均可采納協(xié)議并具體實現(xiàn)這組方法垒酬。
Objective-C在NeXT時期曾經(jīng)試圖引入多重繼承的概念,但由于協(xié)議的出現(xiàn)而沒有實現(xiàn)之件炉。
協(xié)議類似于Java與C#語言中的“接口”勘究。在Objective-C中,有兩種定義協(xié)議的方式:由編譯器保證的“正式協(xié)議”斟冕,以及為特定目的設定的“非正式協(xié)議”口糕。
非正式協(xié)議為一個可以選擇性實現(xiàn)的一系列方法列表。非正式協(xié)議雖名為協(xié)議磕蛇,但實際上是掛于NSObject上的未實現(xiàn)分類(Unimplemented Category)的一種稱謂景描,Objetive-C語言機制上并沒有非正式協(xié)議這種東西,OSX 10.6版本之后由于引入@optional關(guān)鍵字秀撇,使得正式協(xié)議已具備同樣的能力超棺,所以非正式協(xié)議已經(jīng)被廢棄不再使用。
正式協(xié)議類似于Java中的"接口"呵燕,它是一系列方法的列表棠绘,任何類都可以聲明自身實現(xiàn)了某個協(xié)議。在Objective-C 2.0之前,一個類必須實現(xiàn)它聲明匹配的協(xié)議中的所有方法氧苍,否則編譯器會報告錯誤夜矗,表明這個類沒有實現(xiàn)它聲明匹配的協(xié)議中的全部方法。Objective-C 2.0版本允許標記協(xié)議中某些方法為可選的(Optional)让虐,這樣編譯器就不會強制實現(xiàn)這些可選的方法紊撕。
協(xié)議經(jīng)常應用于Cocoa中的委托及事件觸發(fā)。例如文本框類通常會包括一個委托(delegate)對象,該對象可以實現(xiàn)一個協(xié)議,該協(xié)議中可能包含一個實現(xiàn)文字輸入的自動完成方法牙甫。若這個委托對象實現(xiàn)了這個方法差购,那么文本框類就會在適當?shù)臅r候觸發(fā)自動完成事件,并調(diào)用這個方法用于自動完成功能。
Objective-C中協(xié)議的概念與Java中接口的概念并不完全相同,即一個類可以在不聲明它匹配某個協(xié)議的情況下,實現(xiàn)這個協(xié)議所包含的方法逞泄,也即實質(zhì)上匹配這個協(xié)議,而這種差別對外部代碼而言是不可見的拜效。正式協(xié)議的聲明不提供實現(xiàn)喷众,它只是簡單地表明匹配該協(xié)議的類實現(xiàn)了該協(xié)議的方法,保證調(diào)用端可以安全調(diào)用方法紧憾。
語法
協(xié)議以關(guān)鍵字@protocol作為區(qū)塊起始到千,@end結(jié)束,中間為方法列表赴穗。
@protocol Locking- (void)lock;- (void)unlock;@end
這是一個協(xié)議的例子憔四,多線程編程中經(jīng)常要確保一份共享資源同時只有一個線程可以使用,會在使用前給該資源掛上鎖 般眉,以上即為一個表明有“鎖”的概念的協(xié)議了赵,協(xié)議中有兩個方法,只有名稱但尚未實現(xiàn)甸赃。
下面的SomeClass宣稱他采納了Locking協(xié)議:
@interface SomeClass : SomeSuperClass <Locking>@end
一旦SomeClass表明他采納了Locking協(xié)議柿汛,SomeClass就有義務實現(xiàn)Locking協(xié)議中的兩個方法。
@implementation SomeClass- (void)lock { // 實現(xiàn)lock方法...}- (void)unlock { // 實現(xiàn)unlock方法...}@end
由于SomeClass已經(jīng)確實遵從了Locking協(xié)議埠对,故調(diào)用端可以安全的發(fā)送lock或unlock消息給SomeClass實體變量络断,不需擔心他沒有辦法回應消息。
插件是另一個使用抽象定義的例子项玛,可以在不關(guān)心插件的實現(xiàn)的情況下定義其希望的行為貌笨。
動態(tài)類型[編輯]
類似于Smalltalk,Objective-C具備動態(tài)類型:即消息可以發(fā)送給任何對象實體襟沮,無論該對象實體的公開接口中有沒有對應的方法锥惋。對比于C++這種靜態(tài)類型的語言昌腰,編譯器會擋下對(void)指針調(diào)用方法的行為。但在Objective-C中净刮,你可以對id發(fā)送任何消息(id很像void,但是被嚴格限制只能使用在對象上)硅则,編譯器僅會發(fā)出“該對象可能無法回應消息”的警告淹父,程序可以通過編譯,而實際發(fā)生的事則取決于運行期該對象的真正形態(tài)怎虫,若該對象的確可以回應消息暑认,則依舊運行對應的方法。
一個對象收到消息之后大审,他有三種處理消息的可能手段蘸际,第一是回應該消息并運行方法,若無法回應徒扶,則可以轉(zhuǎn)發(fā)消息給其他對象粮彤,若以上兩者均無,就要處理無法回應而拋出的例外姜骡。只要進行三者之其一导坟,該消息就算完成任務而被丟棄。若對“nil”(空對象指針)發(fā)送消息圈澈,該消息通常會被忽略惫周,取決于編譯器選項可能會拋出例外。
雖然Objective-C具備動態(tài)類型的能力康栈,但編譯期的靜態(tài)類型檢查依舊可以應用到變量上递递。以下三種聲明在運行時效力是完全相同的,但是三種聲明提供了一個比一個更明顯的類型信息啥么,附加的類型信息讓編譯器在編譯時可以檢查變量類型登舞,并對類型不符的變量提出警告。
下面三個方法悬荣,差異僅在于參數(shù)的形態(tài):
- setMyValue:(id) foo;
id形態(tài)表示參數(shù)“foo”可以是任何類的實例逊躁。
- setMyValue:(id <aProtocol>) foo;
id<aProtocol>表示“foo”可以是任何類的實例,但必須采納“aProtocol”協(xié)議隅熙。
- setMyValue:(NSNumber*) foo;
該聲明表示“foo”必須是“NSNumber”的實例稽煤。
動態(tài)類型是一種強大的特性。在缺少泛型的靜態(tài)類型語言(如Java 5以前的版本)中實現(xiàn)容器類時囚戚,程序員需要寫一種針對通用類型對象的容器類酵熙,然后在通用類型和實際類型中不停的強制類型轉(zhuǎn)換。無論如何驰坊,類型轉(zhuǎn)換會破壞靜態(tài)類型匾二,例如寫入一個“整數(shù)”而將其讀取為“字符串”會產(chǎn)生運行時錯誤。這樣的問題被泛型解決,但容器類需要其內(nèi)容對象的類型一致察藐,而對于動態(tài)類型語言則完全沒有這方面的問題皮璧。
轉(zhuǎn)發(fā)[編輯]
Objective-C允許對一個對象發(fā)送消息,不管它是否能夠響應之分飞。除了響應或丟棄消息以外悴务,對象也可以將消息轉(zhuǎn)發(fā)到可以響應該消息的對象。轉(zhuǎn)發(fā)可以用于簡化特定的設計模式譬猫,例如觀測器模式或代理模式讯檐。
Objective-C運行時在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
響應方法:
- (retval_t) performv:(SEL) sel :(arglist_t) args; // with GCC- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems
希望實現(xiàn)轉(zhuǎn)發(fā)的對象只需用新的方法覆蓋以上方法來定義其轉(zhuǎn)發(fā)行為。無需重寫響應方法performv::
染服,由于該方法只是單純的對響應對象發(fā)送消息并傳遞參數(shù)别洪。其中,SEL
類型是Objective-C中消息的類型柳刮。
例子[編輯]
這里包括了一個演示轉(zhuǎn)發(fā)的基本概念的程序示例挖垛。
Forwarder.h
import <objc/Object.h>@interface Forwarder : Object{ id recipient; //該對象是我們希望轉(zhuǎn)發(fā)到的對象。}@property (assign, nonatomic) id recipient;@end
Forwarder.m
import "Forwarder.h"@implementation Forwarder@synthesize recipient;- (retval_t) forward: (SEL) sel : (arglist_t) args{ /* *檢查轉(zhuǎn)發(fā)對象是否響應該消息秉颗。 *若轉(zhuǎn)發(fā)對象不響應該消息晕换,則不會轉(zhuǎn)發(fā),而產(chǎn)生一個錯誤站宗。 */ if([recipient respondsTo:sel]) return [recipient performv: sel : args]; else return [self error:"Recipient does not respond"];}
Recipient.h
import <objc/Object.h>// A simple Recipient object.@interface Recipient : Object- (id) hello;@end
Recipient.m
import "Recipient.h"@implementation Recipient- (id) hello{ printf("Recipient says hello!\n"); return self;}@end
main.m
import "Forwarder.h"#import "Recipient.h"int main(void){ Forwarder *forwarder = [Forwarder new]; Recipient recipient = [Recipient new]; forwarder.recipient = recipient; //Set the recipient. / *轉(zhuǎn)發(fā)者不響應hello消息闸准!該消息將被轉(zhuǎn)發(fā)到轉(zhuǎn)發(fā)對象。 *(若轉(zhuǎn)發(fā)對象響應該消息) */ [forwarder hello]; return 0;}
腳注[編輯]
利用GCC編譯時梢灭,編譯器報告:
$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjcmain.m: In function main':main.m:12: warning:
Forwarder' does not respond to `hello'$
如前文所提到的夷家,編譯器報告Forwarder
類不響應hello消息。在這種情況下敏释,由于實現(xiàn)了轉(zhuǎn)發(fā)库快,可以忽略這個警告。 運行該程序產(chǎn)生如下輸出:
$ ./a.outRecipient says hello!
類別 (Category)[編輯]
在Objective-C的設計中钥顽,一個主要的考慮即為大型代碼框架的維護义屏。結(jié)構(gòu)化編程的經(jīng)驗顯示,改進代碼的一種主要方法即為將其分解為更小的片段蜂大。Objective-C借用并擴展了Smalltalk實現(xiàn)中的“分類”概念闽铐,用以幫助達到分解代碼的目的。[3]
一個分類可以將方法的實現(xiàn)分解進一系列分離的文件奶浦。程序員可以將一組相關(guān)的方法放進一個分類兄墅,使程序更具可讀性。舉例來講澳叉,可以在字符串類中增加一個名為“拼寫檢查”的分類隙咸,并將拼寫檢查的相關(guān)代碼放進這個分類中沐悦。
進一步的,分類中的方法是在運行時被加入類中的五督,這一特性允許程序員向現(xiàn)存的類中增加方法藏否,而無需持有原有的代碼,或是重新編譯原有的類充包。例如若系統(tǒng)提供的字符串類的實現(xiàn)中不包含拼寫檢查的功能副签,可以增加這樣的功能而無需更改原有的字符串類的代碼。
在運行時误证,分類中的方法與類原有的方法并無區(qū)別继薛,其代碼可以訪問包括私有類成員變量在內(nèi)的所有成員變量修壕。
若分類聲明了與類中原有方法同名的函數(shù)愈捅,則分類中的方法會被調(diào)用。因此分類不僅可以增加類的方法慈鸠,也可以代替原有的方法蓝谨。這個特性可以用于修正原有代碼中的錯誤,更可以從根本上改變程序中原有類的行為青团。若兩個分類中的方法同名譬巫,則被調(diào)用的方法是不可預測的。
其它語言也嘗試了通過不同方法增加這一語言特性督笆。TOM在這方面走的更遠芦昔,不僅允許增加方法,更允許增加成員變量娃肿。也有其它語言使用面向聲明的解決方案咕缎,其中最值得注意的是Self語言。
C#與Visual Basic.NET語言以擴展函數(shù)的與不完全類的方式實現(xiàn)了類似的功能料扰。Ruby與一些動態(tài)語言則以"monkey patch"的名字稱呼這種技術(shù)凭豪。
使用分類的例子[編輯]
這個例子創(chuàng)建了Integer
類,其本身只定義了integer屬性晒杈,然后增加了兩個分類Arithmetic
與Display
以擴展類的功能嫂伞。雖然分類可以訪問類的私有成員,但通常利用屬性的訪問方法來訪問是一種更好的做法拯钻,可以使得分類與原有類更加獨立帖努。這是分類的一種典型應用—另外的應用是利用分類來替換原有類中的方法,雖然用分類而不是繼承來替換方法不被認為是一種好的做法粪般。
Integer.h
import <objc/Object.h>@interface Integer : Object{@private int integer;}@property (assign, nonatomic) integer;@end
Integer.m
import "Integer.h"@implementation Integer@synthesize integer;@end
Arithmetic.h
import "Integer.h"@interface Integer(Arithmetic)- (id) add: (Integer *) addend;- (id) sub: (Integer *) subtrahend;@end
Arithmetic.m
import "Arithmetic.h"@implementation Integer(Arithmetic)- (id) add: (Integer *) addend{ self.integer = self.integer + addend.integer; return self;}- (id) sub: (Integer *) subtrahend{ self.integer = self.integer - subtrahend.integer; return self;}@end
Display.h
import "Integer.h"@interface Integer(Display)- (id) showstars;- (id) showint;@end
Display.m
import "Display.h"@implementation Integer(Display)- (id) showstars{ int i, x = self.integer; for(i=0; i < x; i++) printf("*"); printf("\n"); return self;}- (id) showint{ printf("%d\n", self.integer); return self;}@end
main.m
import "Integer.h"#import "Arithmetic.h"#import "Display.h"intmain(void){ Integer *num1 = [Integer new], *num2 = [Integer new]; int x; printf("Enter an integer: "); scanf("%d", &x); num1.integer = x; [num1 showstars]; printf("Enter an integer: "); scanf("%d", &x); num2.integer = x; [num2 showstars]; [num1 add:num2]; [num1 showint]; return 0;}
注釋[編輯]
可以利用以下命令來編譯:
gcc -x objective-c main.m Integer.m Arithmetic.m Display.m -lobjc
在編譯時間然磷,可以利用省略#import "Arithmetic.h"
與[num1 add:num2]
命令,以及Arithmetic.m
文件來實驗刊驴。程序仍然可以運行姿搜,這表明了允許動態(tài)的寡润、按需的加載分類;若不需要某一分類提供的功能舅柜,可以簡單的不編譯之梭纹。
扮演[編輯]
Objective-C允許一個類在程序中完全替換另一個類,這種行為稱為前者“扮演”目標類致份。
注意:類的扮演在Mac OS X v10.5中被廢棄变抽,在64位運行時中不可用。
import[編輯]
在C語言中氮块,#include
預處理指令總是使被包含的文件內(nèi)容被插入指令點绍载。在Objective-C中,類似的指令#import
保證一個文件只會被包含一次滔蝉,類似于一般頭文件中的
ifndef XXX#define XXX ...#endif
慣用法击儡,或MSVC中的
pragma once
語言變化[編輯]
Objective-C++[編輯]
Objective-C++是GCC的一個前端,它可以編譯混合了C++與Objective-C語法的源文件蝠引。Objective-C++是C++的擴展阳谍,類似于Objective-C是C的擴展。由于在融合C++與Objective-C兩種語言的特性方面沒有做特別的工作螃概,因此有以下限制:
C++類不能從Objective-C類繼承矫夯,反之亦然。
Objective-C定義內(nèi)部不能定義C++命名空間吊洼。
Objective-C類的成員變量不能包括不含默認構(gòu)造函數(shù)和/或含有虛方法的C++類對象训貌,但使用C++類指針并無如此限制(可以在 -init方法中對之進行初始化)。
C++“傳遞值”的特性不能用在Objective-C對象上冒窍,而只能傳遞其指針递沪。
Objective-C聲明不能存在在C++模板聲明中,反之亦然超燃。但Objective-C類型可以用在C++模板的參數(shù)中区拳。
Objective-C和C++的錯誤處理語句不同,各自的語句只能處理各自的錯誤意乓。
Objective-C錯誤使得C++對象被退出時樱调,C++析構(gòu)函數(shù)不會被調(diào)用。新的64位運行時解決了這個問題届良。[4]
Objective-C 2.0[編輯]
在2006年7月蘋果全球開發(fā)者會議中笆凌,Apple宣布了“Objective-C 2.0”的發(fā)布,其增加了“現(xiàn)代的垃圾收集士葫,語法改進[5]
乞而,運行時性能改進[6]
,以及64位支持”慢显。2007年10月發(fā)布的Mac OS X v10.5中包含了Objective-C 2.0的編譯器爪模。
垃圾收集[編輯]
Objective-C 2.0提供了一個可選的垃圾收集器欠啤。在向后兼容模式中,Objective-C運行時會將引用計數(shù)操作屋灌,例如“retain”與“release”變?yōu)?a target="_blank" rel="nofollow">無操作洁段。當垃圾收集啟用時,所有的對象都是收集器的工作對象共郭。普通的C指針可以以“__strong”修飾祠丝,標記指針指向的對象仍在使用中。被標記為“__weak”的指針不被計入收集器的計數(shù)中除嘹,并在對象被回收時改寫為“nil”写半。iOS上的Objective-C 2.0實現(xiàn)中不包含垃圾收集器。垃圾收集器運行在一個低優(yōu)先級的后臺線程中尉咕,并可以在用戶動作時暫停叠蝇,從而保持良好的用戶體驗。[7]
屬性[編輯]
Objective-C 2.0引入了新的語法以聲明變量為屬性龙考,并包含一可選定義以配置訪問方法的生成蟆肆。屬性總是為公共的矾睦,其目的為提供外部類訪問(也可能為只讀)類的內(nèi)部變量的方法晦款。屬性可以被聲明為“readonly”,即只讀的枚冗,也可以提供儲存方法包括“assign”缓溅,“copy”或“retain”(簡單的賦值、復制或增加1引用計數(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;@end
屬性的訪問方法由@synthesize關(guān)鍵字來實現(xiàn),它由屬性的聲明自動的產(chǎn)生一對訪問方法稚疹。另外居灯,也可以選擇使用@dynamic關(guān)鍵字表明訪問方法會由程序員手工提供。
@implementation Person@synthesize name;@dynamic age;-(id)initWithAge:(int)initAge{ age = initAge; // 注意:直接賦給成員變量内狗,而非屬性 return self;}-(int)age{ return 29; // 注意:并非返回真正的年齡}@end
屬性可以利用傳統(tǒng)的消息表達式怪嫌、點表達式或"valueForKey:"/"setValue:forKey:"方法對來訪問。
Person *aPerson = [[Person alloc] initWithAge: 53];aPerson.name = @"Steve"; // 注意:點表達式柳沙,等于[aPerson setName: @"Steve"];NSLog(@"Access by message (%@), dot notation(%@), property name(%@) and direct instance variable access (%@)", [aPerson name], aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);
為了利用點表達式來訪問實例的屬性岩灭,需要使用“self”關(guān)鍵字:
-(void) introduceMyselfWithProperties:(BOOL)useGetter{ NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name)); // NOTE: getter vs. ivar access}
類或協(xié)議的屬性可以被動態(tài)的讀取。
int i;int propertyCount = 0;objc_property_t propertyList = class_copyPropertyList([aPerson class], &propertyCount);for ( i=0; i < propertyCount; i++ ) { objc_property_t thisProperty = propertyList + i; const char propertyName = property_getName(thisProperty); NSLog(@"Person has a property: '%s'", propertyName);}
快速枚舉[編輯]
比起利用NSEnumerator對象或在集合中依次枚舉赂鲤,Objective-C 2.0提供了快速枚舉的語法噪径。在Objective-C 2.0中柱恤,以下循環(huán)的功能是相等的,但性能特性不同找爱。
// 使用NSEnumeratorNSEnumerator *enumerator = [thePeople objectEnumerator];Person *p;while ( (p = [enumerator nextObject]) != nil ) { NSLog(@"%@ is %i years old.", [p name], [p age]);}
// 使用依次枚舉for ( int i = 0; i < [thePeople count]; i++ ) { Person *p = [thePeople objectAtIndex:i]; NSLog(@"%@ is %i years old.", [p name], [p age]);}
// 使用快速枚舉for (Person *p in thePeople) { NSLog(@"%@ is %i years old.", [p name], [p age]);}
快速枚舉可以比標準枚舉產(chǎn)生更有效的代碼膨更,由于枚舉所調(diào)用的方法被使用NSFastEnumeration協(xié)議提供的指針算術(shù)運算所代替了。[8]
語言分析[編輯]
Objective-C是非辰稍剩“實際”的語言荚守。它用一個很小的、用C寫成的運行庫练般,使得應用程序的大小增加很少矗漾,與此相比,大部分OO系統(tǒng)需要極大的運行時虛擬機來執(zhí)行薄料。ObjC寫成的程序通常不會比其源代碼和庫(通常無需包含在軟件發(fā)行版本中)大太多敞贡,不會像Smalltalk系統(tǒng),即使只是打開一個窗口也需要大量的容量摄职。由于Obj-C的動態(tài)類型特征誊役,Obj-C不能對方法進行內(nèi)聯(lián)(inline)一類的優(yōu)化,使得Obj-C的應用程序一般比類似的C或C++程序更大谷市。
Obj-C可以在現(xiàn)存C編譯器基礎(chǔ)上實現(xiàn)(在GCC中蛔垢,Obj-C最初作為預處理器引入,后來作為模塊存在)迫悠,而不需要編寫一個全新的編譯器鹏漆。這個特性使得Obj-C能利用大量現(xiàn)存的C代碼、庫创泄、工具和編程思想等資源∫樟幔現(xiàn)存C庫可以用Obj-C包裝器來提供一個Obj-C使用的OO風格界面包裝。
以上這些特性極大地降低了進入Obj-C的門檻鞠抑,這是1980年代Smalltalk在推廣中遇到的最大問題饭聚。
Objective-C的最初版本并不支持垃圾回收(garbage collection)。在當時這是爭論的焦點之一搁拙,很多人考慮到Smalltalk回收時有漫長的“死亡時間”秒梳,令整個系統(tǒng)失去功用,Objective-C為避免此問題才不擁有這個功能感混。某些第三方版本加入了這個功能(尤是GNUstep)端幼,蘋果公司也在其Mac OS X 10.5中提供了實現(xiàn)。
另一個廣受批評的問題是ObjC不包括名字空間機制(namespace mechanism)弧满。取而代之的是程序員必須在其類別名稱加上前綴婆跑,由于前綴往往較短(相比命名空間),這時常引致沖突庭呜。在2007年滑进,在Cocoa編程環(huán)境中犀忱,所有Mac OS X類別和函數(shù)均有“NS”作為前綴,例如NSObject或NSButton來清楚分辨它們屬于Mac OS X核心扶关;使用“NS”是由于這些類別的名稱在NeXTSTEP開發(fā)時定下阴汇。
雖然Objective-C是C的嚴格超集,但它也不視C的基本類型為第一級的對象节槐。
和C++不同搀庶,Objective-C不支持運算符重載(它不支持ad-hoc多態(tài))。亦與C++不同铜异,但和Java相同哥倔,Objective-C只容許對象繼承一個類別(不設多重繼承)。Categories和protocols不但可以提供很多多重繼承的好處揍庄,而且沒有很多缺點咆蒿,例如額外運行時間過重和二進制不兼容。[來源請求]
由于Obj-C使用動態(tài)運行時類型蚂子,而且所有的方法都是函數(shù)調(diào)用(有時甚至連系統(tǒng)調(diào)用(syscalls)也如此)沃测,很多常見的編譯時性能優(yōu)化方法都不能應用于Obj-C(例如:內(nèi)聯(lián)函數(shù)、常數(shù)傳播食茎、交互式優(yōu)化蒂破、純量替換與聚集等)。這使得Obj-C性能劣于類似的對象抽象語言(如C++)董瞻。不過Obj-C擁護者認為Obj-C本就不應應用于C++或Java常見的底層抽象寞蚌,Obj-C的應用方向是對性能要求不大的應用
參考文獻[編輯]
^ Mac OS X 10.6 Snow Leopard: the Ars Technica review, page 5
^ Adam Treat, mkspecs and patches for LLVM compile of Qt4
^ Example of categories concept
^ Using C++ With Objective-C in Mac OS X Reference Library, last retrieved in 2010-02-10.
^ Objective-C 2.0: more clues. Lists.apple.com. 2006-08-10 [2010-05-30].
^ Re: Objective-C 2.0. Lists.apple.com. [2010-05-30].
^ Apple Computer, Inc. Leopard Technology Series for Developers: Objective-C 2.0 Overview. Developer.apple.com. 2007-11-06 [2010-05-30].
^ Apple, Inc. Fast Enumeration. apple.com. 2009 [2009-12-31].
外部鏈接[編輯]
蘋果官方Objective-C開發(fā)說明文檔