C++開發(fā)者快速學習Objective-C語言核心語法
本文將Objective-C討論了語言的核心語法。這部分開始詳述一些具體的語法。正如你期待的一樣,涉及到了定義和類。
類并不是特殊的
在Smalltalk中,類是具有一些特性的對象。在Objective-C中也一樣。一個類是一個對象,對象回應消息。Objective-C和C++都分離了對象分配和初始化。
在C++中,對象分配通過新的操作。在Objective-C中,這樣的操作是通過給類發(fā)送分配消息—調(diào)用malloc()或者一個等價。
C++中的初始化是通過調(diào)用一個與類同名的函數(shù)。Objective-C并沒有區(qū)別初始化方法和其他方法,但出于慣例缺省的初始化方法就是初始化。
當你聲明一個方法讓實例去回應,聲明通常已“-”開頭,并且“+”用作類的方法。在文檔中對這些消息使用一些前綴是很普遍的,所以你也可以說+alloc和-init來暗示alloc是傳給一個類,init傳給實例。
類在Objective-C中,正如在其他一些面向?qū)ο笳Z言,都是對象工廠。大多數(shù)類不用自行實現(xiàn)+alloc,而是從他們的父類中繼承。在 NSObject中,父類在大多數(shù)Objective-C程序中,+alloc方法調(diào)用+allocWithZone:.使NSZone作為一個參數(shù),一 個C結(jié)構(gòu)包含對象分配的一些策略?;仡?9世紀80年代,當Objective-C用在NeXTstep來實現(xiàn)設備驅(qū)動和只有8MB內(nèi)存25MHZ的 CPU機器的GUI上面時,NSZone對優(yōu)化非常重要。同時,這或多或少的被Objective-C程序員所忽視。(很有可能成為象NUMA構(gòu)架一樣流 行,更普遍。)
眾多優(yōu)秀的特性之一就是對象創(chuàng)建語義是由庫定義的并且語言不是類簇的思想。當你傳一個-init消息給對象時,它返回一個初始化對象。這可能是你發(fā)送消息的那個對象,但不一定肯定就是。這和其他初始化程序一致的。很有可能一些公共類的特殊子類在不同數(shù)據(jù)上更有效。
實現(xiàn)這個特性的通用方法叫做isa-swizzling。正如我前述,Objective-C對象是C結(jié)構(gòu),這些結(jié)構(gòu)***個元素是指向類的指針。這 個元素是可存取的,正如其他實例變量一樣;你可以在運行時通過分配新值來改變對象的類。當然,如果你對對象的類設置在內(nèi)存中有著不同的布局,這些設置可能 嚴重錯誤。
然而,你可以通過一個父類來定義布局和通過子集的集合定義行為,舉例來說,這個技術用在標準化字符串類(NSString),它對不同的文本字符集、靜態(tài)事物和其它一些有著各種各樣的實例。
因為類是對象,你可以象操作對象一樣操作他們。舉例來說,你可以把他們放在集合。當我有一些輸入事件需要通過不同的類的實例來處理時我就使用這種格 式。你需要創(chuàng)建一個目錄映射事件命名到類,然后為每一個輸入事件實例化一個對象。如果你在一個庫中這么做,它允許代碼的使用者輕松的注冊屬于他們自己的句 柄。
類型和指針
Objective-C沒有公開允許在堆棧上定義對象。但并不是真的—很有可能在堆棧上定義對象,但有些困難,因為它破壞了對內(nèi)存管理的一種假設。 結(jié)果,每一個Objective-C對象都是一個指針。一些類型由Objective-C定義;這些類型在頭部定義作為C類型。
在Objective-C中最普遍的3種類型就是id,Class和SEL。id就是指向Objective-C對象的指針,它等價于C語言中的void*,你可以映射任何對象指針類型指向它并且映射他指向其它的對象指針類型。
你可以傳任何消息給id,但如果不支持的話會返回一個運行時異常。
類是指向Objective-C類的指針。類是對象,所以也可以接收消息。類名是一種類型,不是可變的。標識符NSObject是一個NSObject實例的類型,但也可作為消息接受者。你可以獲得一個類,如下:
- [NSObject class];
發(fā)送一個+class消息給NSObject類,然后返回一個指向代表類的類結(jié)構(gòu)指針。
這對我們回顧是非常有用的[FS:PAGE],正如我們在這個系列第二部分看到的一樣。
第三種類型SEL,代表一個選擇器—一個代表方法名的抽象。你可以在編譯時通過@selector()直接創(chuàng)建,或在運行時通過C字符串調(diào)用運行時 庫函數(shù),或用OpenStep NSSelectorFromString()函數(shù),這個函數(shù)給Objective-C字符串一個選擇器。這個技術允許你通過名字調(diào)用方法。你可以在C中 通過使用類似dlsym(),但在C++中非常不同。在Objective-C中,你可以做的如下:
- [object perfomSelector:@selector(doSomething)];
這等價于如下:
- [object doSomething];
顯然,第二種格式速度稍微快些,因為***種傳送兩個消息。后面,我們會看到通過選擇器處理的一些細節(jié)。
C++沒有與id相同的類型。因為對象總是可以類型化的。在Objective-C,你可以選擇類型系統(tǒng)。下面的兩種都是有效的:
- id object = @”a string”;
- NSString *string = @”a string”;
常量字符串實際上是NSConstantString類的一個實例,NSConstantString類是NSString的子類。將它引用到 NSString* 使編譯時對消息進行類型檢查和存儲公共實例變量(這在Objective-C從未使用過)。注意你可以通過如下改變這一設置:
- NSArray *array = (NSArray*)string;
如果給數(shù)組發(fā)送消息,編譯器將會檢查NSArray能接收的消息。這并不是非常有用,因為對象是一個字符串。如果發(fā)送一個NSArray和NSString實現(xiàn)的消息,可能會有作用。如果你發(fā)送的消息NSString沒有實現(xiàn),一個異常將會拋出。
強調(diào)Objective-C和C++的不同的這件事看起來比較奇怪。Objective-C有類型-值語法,而C++有類型-變量語法。在Objective-C,對象類型是對象專有的一種屬性。在C++,類型取決于變量的類型。
在C++中,當你指派一個指針指向一個對象到一個變量定義一個指向父類的指針,兩個指針可能沒有相同的數(shù)值(這可以通過多繼承實現(xiàn),而Objective-C不支持這種。)
定義類
Objective-C類定義有一個接口和一個實現(xiàn)部分。與C++有相似的地方,但兩個稍微有些混。
Objective-C中的接口僅定義位并且明確的需要公開。對于實現(xiàn)的原因,這包括私有實例變量在大部分的實現(xiàn)中,因為你無法繼承一個類除非你知道它多大。最近的一些實現(xiàn),象Apple的64位運行時則沒有這種限制。
Objective-C對象的接口如下:
- @interface AnObject : NSObject
- {
- @private
- int integerivar
- @public
- id anotherObject;
- }
- + (id) aClassMethod;
- - (id) anInstanceMethod:(NSString*)aString with:(id)anObject
- @end
***行包含3個部分。標識符AnObject 是新類的名字。冒號后面的名字是NSObject。(這是可選的,但每一個Objective-C 對象都應拓展NSObject)。在括號內(nèi)的名字是協(xié)議——與Java中的接口相似——通過類來實現(xiàn)。
正如C++實例變量(C++中的域)可以存取修飾符,不象C++,這些修飾符以@為前綴是為了避免與C標識符沖突。
Objective-C不支持多繼承,所以只有一個父類。所以,對象***部分的布局總是與父類實例的布局一致。這在過去常常定義為動態(tài),意味著改變 類中實例變量需要它所有子類重新編譯。在較新的運行時這種限定并不要求,在存取實例實例變量上開支稍微大些。這種決策的另一個影響就是 Objective-C其他特性中的一個。
- struct_AnObject
- {
- @defs(AnObject);
- };
@def表示著對特定對象所有域都插入這種結(jié)構(gòu),所以struct_AnObject 和AnObject類的實例有著相同的內(nèi)存結(jié)構(gòu)。舉個例子來說,你可以通過這種規(guī)則可以直接存取實例變量。一個通常的用法就是允許C函數(shù)直接操作 Objective-C對象,是基于性能原因。
正如我前面暗示的,與這個特性相關的另一件事就是可以在堆棧上創(chuàng)建對象。因為結(jié)構(gòu)和對象在[FS:PAGE]內(nèi)存布局中有著相同的結(jié)構(gòu),你可以簡單的創(chuàng)建 結(jié)構(gòu),設置他的指針指向正確的類,然后映射一個指針指向一個對象指針。然后你可以當做對象來使用,雖然你不得不小心沒有什么可以保持指針不越界。(現(xiàn)實世 界中我從沒有使用這種方法,僅僅理論上可能。)
不象C++,Objective-C沒有私有的或受保護的方法。Objective-C對象上的任何方法可以被其他對象調(diào)用。如果你在接口中沒有聲明方法,就是非正式私有的。將會得到運行時警告:對象不回應這種消息,但是你任然可以調(diào)用它。
接口和C中頭部聲明很相似。但它仍然需要一個實現(xiàn),這并不奇怪,可以使用@implementation來定義。
- @implementation AnObject
- + (id) aClassMethod
- {
- ...
- }
- - (id) anInstanceMethod:(NSString*)aString with:(id)anObject
- {
- ...
- }
- @end
注意參數(shù)類型是特定的,在括號里。這是從C重用映射語法來展示值映射到類型;他們可能不是類型。準確來說當映射時應用相同的規(guī)則。這意味著映射在不兼容對象指針類型間會導致一個警告(不是錯誤)。
內(nèi)存管理
傳統(tǒng)的,Objective-C不提供任何內(nèi)存管理。在早期版本中,對象類實現(xiàn)一個+new方法調(diào)用malloc()來創(chuàng)建一個新對象。當使用完這 個對象,傳一個-free消息。任何一個對象從NSObject繼承回應一個-retain和-release消息。當你使用完這個對象,你傳一個 -free消息。OpenStep添加了參考計算。
每一個從NSObject繼承的對象都回應-retain和-release消息。當你想要保留一個指向?qū)ο蟮闹羔槪憧梢园l(fā)送一個-retain消息。當你使用完以后,你可以發(fā)送一個-release消息。
這個設計有個細微問題。通常你不需要保持一個指向?qū)ο蟮闹羔?,但是你也不想釋放。一個典型的例子在返回一個對象時候,調(diào)用者需要保持指向?qū)ο蟮闹羔?,但你不想這么做。
這個問題的解決方案就是NSAutoreleasePool類。加上-retain和-release,NSObject也回應 -autorelease消息。當你發(fā)送其中一個,和現(xiàn)前的自動釋放池一同注冊。當這個池對象被注銷,它發(fā)送一個-release消息給每個對象,而對象 在這之前先收到-autorelease消息。在OpenStep應用中,一個NSAutoreleasePool實例在循環(huán)開始的時候創(chuàng)建,在結(jié)束的時 候銷毀。你也可以創(chuàng)建屬于你自己的實例來自動釋放對象。
這個機制減少了一些C++所需的復制。其實也不值得這么做,在Objective-C,易變性是對象的屬性,不是參考。在C++,有常量指針和非常量指針。不允許在常量對象上調(diào)用非常量方法。這保證不了對象不會被改變——僅僅因為你不想改變。
在Objective-C中,一個常態(tài)模式定義了一個不變的類和可變的子類。NSString就是一個典型例子;
它有一個可變的子類NSMutableString。如果你得到NSString并且想保存下來,你可以傳一個-retain消息并且不用復制操作 就可以保存指針。相反地,你可以傳一個+stringWithString:message給NSString。不管這個參數(shù)是否可變都會檢查并返回原始 指針。
在Apple和GNU運行時,Objective-C都支持存儲性的垃圾回收,這會避免對-retain和-release的需要。在現(xiàn)存的框架中對語言的附加并不總是很好的支持的,并且在用的時候需要格外小心。
總結(jié)
既然我們已經(jīng)瀏覽了Objective-C語言的核心,在這部分的總結(jié)我們將會看到更多的一些高級話題。
原文出處: 折酷吧