本頁使用了標題或全文手工轉換

Objective-C

維基百科,自由的百科全書
前往: 導覽搜尋
Objective-C
編程範型 物件導向
設計者 布萊德·考克斯Tom Love
實作者 蘋果公司
面市時間 1983
最近釋出日期 Objective-C 2.1[1](2007年10月26日)
型態系統 靜態型別動態型別弱型別
作業系統 跨平台
許可證 封閉專有

Objective-C是一種通用高階物件導向程式語言。它擴充功能了標準的ANSI C程式語言,將Smalltalk式的訊息傳遞機制加入到ANSI C中。目前主要支援的編譯器GCCClang(採用LLVM作為後端)。

Objective-C的商標權屬於蘋果公司,蘋果公司也是這個程式語言的主要開發者。蘋果在開發NeXTSTEP操作系統時使用了Objective-C,之後被OS XiOS繼承下來。現在Objective-C與Swift是OS X和iOS作業系統、及與其相關的APICocoaCocoa Touch的主要程式語言。

歷史[編輯]

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

1981年 Brad Cox 和 Tom Love 還在 ITT 公司技術中心任職時,接觸到了 SmallTalk語言。Cox 當時對軟體設計和開發問題非常感興趣,他很快地意識到 SmallTalk語言 在系統工程構建中具有無法估量的價值,但同時他和 Tom Love 也明白,目前 ITT 公司的電子通訊工程相關技術中,C 語言被放在很重要的位置。

於是 Cox 撰寫了一個 C 語言的預處理器,打算使 C 語言具備些許 Smalltalk 的本領。Cox 很快地實現了一個可用的 C 語言擴展,此即為 Objective-C語言的前身。到了 1983 年,Cox 與 Love 合夥成立了 Productivity Products International(PPI)公司,將 Objective-C 及其相關函式庫商品化販售,並在之後將公司改名為StepStone。1986年,Cox 出版了一本關於 Objective-C 的重要著作《Object-Oriented Programming, An Evolutionary Approach》,書內詳述了 Objective-C 的種種設計理念。

1988年,斯蒂夫·賈伯斯(Steve Jobs)離開蘋果公司後成立了 NeXT Computer 公司,NeXT 公司買下 Objective-C 語言的授權,並擴展了著名的開源編譯器GCC 使之支援 Objective-C 的編譯,基於 Objective-C 開發了 AppKitFoundation Kit 等函式庫,作為 NeXTSTEP 的的用戶介面與開發環境的基礎。雖然 NeXT 工作站後來在市場上失敗了,但 NeXT 上的軟體工具卻在業界中被廣泛讚揚。這促使 NeXT 公司放棄硬體業務,轉型為銷售NeXTStep(以及OpenStep)平台為主的軟體公司。

1992年,自由軟體基金會的 GNU 開發環境增加了對 Objective-C 的支援。1994年,NeXT Computer公司和Sun Microsystem聯合發布了一個針對 NEXTSTEP 系統的標準典範,名為 OPENSTEP。OPENSTEP 在自由軟體基金會的實現名稱為 GNUstep。1996年12月20日,蘋果公司宣布收購 NeXT Software 公司,NEXTSTEP/OPENSTEP環境成為蘋果作業系統下一個主要發行版本OS X的基礎。這個開發環境的版本被蘋果公司稱為Cocoa

2005年,蘋果電腦雇用了克里斯·拉特納及LLVM開發團隊[2]clangLLVM成為蘋果公司在GCC之外的新編譯器選擇,在 Xcode 4.0之後均採用 LLVM 作為默認的編譯器。最新的 Objective-C 特性也都率先在 Clang 上實現。

語法[編輯]

Objective-C是C語言的嚴格超集--任何C語言程式不經修改就可以直接通過Objective-C編譯器,在Objective-C中使用C語言代碼也是完全合法的。Objective-C被描述為蓋在C語言上的薄薄一層,因為Objective-C的原意就是在C語言主體上加入物件導向的特性。Objective-C的物件導向語法源於Smalltalk訊息傳遞風格。所有其他非物件導向的語法,包括變數型別,預處理器(preprocessing),流程控制,函式聲明與呼叫皆與C語言完全一致。但有些C語言語法合法代碼在objective-c中表達的意思不一定相同,比如某些布林運算式,在C語言中返回值為true,但在Objective-C若與yes直接相比較,函式將會出錯,因為在Objective-C中yes的值只表示為1.

Hello World[編輯]

這里示範了一個基礎的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裡,與其說物件互相呼叫方法,不如說物件之間互相傳遞訊息更為精確。此二種風格的主要差異在於呼叫方法/訊息傳遞這個動作。C++裡類別方法的關係嚴格清楚,一個方法必定屬於一個類別,而且在編譯時(compile time)就已經緊密綁定,不可能呼叫一個不存在類別裡的方法。但在Objective-C,類別訊息的關係比較鬆散,呼叫方法視為對物件發送訊息,所有方法都被視為對訊息的回應。所有訊息處理直到執行時(runtime)才會動態決定,並交由類別自行決定如何處理收到的訊息。也就是說,一個類別不保證一定會回應收到的訊息,如果類別收到了一個無法處理的訊息,程式只會拋出異常,不會出錯或崩潰。

C++裡,送一個訊息給物件(或者說呼叫一個方法)的語法如下:

obj.method(argument);

Objective-C則寫成:

[obj method: argument];

此二者並不僅僅是語法上的差異,還有基本行為上的不同。

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

[car fly];

典型的C++意義解讀是「呼叫car類別的fly方法」。若car類別裡頭沒有定義fly方法,那編譯肯定不會通過。但是Objective-C裡,我們應當解讀為「發送出一個fly的訊息給car物件」,fly是訊息,而car是訊息的接收者。car收到訊息後會決定如何回應這個訊息,若car類別內定義有fly方法就執行方法內之代碼,若car內不存在fly方法,則程式依舊可以通過編譯,執行期則拋出異常。

此二種風格各有優劣。C++強制要求所有的方法都必須有對應的動作,且編譯期綁定使得函式呼叫非常快速。缺點是僅能藉由virtual關鍵字提供有限的動態綁定能力。Objective-C天生即具備鴨子型別之動態綁定能力,因為執行期才處理訊息,允許傳送未知訊息給物件。可以送訊息給整個物件集合而不需要一一檢查每個物件的型態,也具備訊息轉送機制。同時空物件nil接受訊息後默認為不做事,所以送訊息給nil也不用擔心程式崩潰。

類的定義與實現[編輯]

Objective-C中強制要求將的定義(interface)與實現(implementation)分為兩個部分。

的定義檔案遵循C語言之慣例以.h為字尾,實現檔案以.m為字尾。

Interface[編輯]

定義部分,清楚定義了的名稱、成員變數和方法。 以關鍵字@interface作為開始,@end作為結束。

@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

方法前面的 +/- 號代表函式的類型:加號(+)代表類別方法(class method),不需要例項就可以呼叫,與C++ 的靜態函式(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定義一個新的方法時,名稱內的冒號(:)代表參數傳遞,不同於C語言以數學函式的括弧來傳遞參數。Objective-C方法使得參數可以夾雜於名稱中間,不必全部附綴於方法名稱的尾端,可以提高程式可讀性。設定顏色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類別的參數,分別代表紅,綠,藍三色。

Implementation[編輯]

實現區段則包含了公開方法的實現,以及定義私有(private)變量及方法。 以關鍵字@implementation作為區段起頭,@end結尾。

@implementation MyObject {
  int memberVar3; //私有實體變數
}

+(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區段可定義實體變數,Implementation區段也可以定義實體變數,兩者的差別在於存取權限的不同,Interface區段內的實體變數默認權限為protected,宣告於implementation區段的實體變數則默認為private,故在Implementation區段定義私有成員更符合物件導向之封裝原則,因為如此類別之私有資訊就不需曝露於公開interface(.h檔案)中。

建立物件[編輯]

Objective-C建立物件需通過alloc以及init兩個訊息。alloc的作用是分配記憶體,init則是初始化物件。 init與alloc都是定義在NSObject里的方法,父物件收到這兩個資訊並做出正確回應後,新物件才建立完畢。以下為範例:

MyObject * my = [[MyObject alloc] init];

在Objective-C 2.0裡,若建立物件不需要參數,則可直接使用new

MyObject * my = [MyObject new];

僅僅是語法上的精簡,效果完全相同。

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

- (id) init {
    if ( self=[super init] ) {   // 必须调用父类的init
        // do something here ...
    }
    return self;
}

協定(Protocol)[編輯]

協議是一組沒有實現的方法列表,任何的類均可採納協議並具體實現這組方法。

Objective-C在NeXT時期曾經試圖引入多重繼承的概念,但由於協定的出現而沒有實現之。

協定類似於JavaC#語言中的「介面」。在Objective-C中,有兩種定義協定的方式:由編譯器保證的「正式協定」,以及為特定目的設定的「非正式協定」。

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

正式協定類似於Java中的"介面",它是一系列方法的列表,任何類都可以聲明自身實現了某個協定。在Objective-C 2.0之前,一個類必須實現它聲明符合的協定中的所有方法,否則編譯器會報告錯誤,表明這個類沒有實現它聲明符合的協定中的全部方法。Objective-C 2.0版本允許標記協定中某些方法為可選的(Optional),這樣編譯器就不會強制實現這些可選的方法。

協議經常應用於Cocoa中的委託及事件觸發。例如文字域類通常會包括一個委託(delegate)物件,該物件可以實現一個協定,該協定中可能包含一個實現文字輸入的自動完成方法。若這個委託物件實現了這個方法,那麼文字域類就會在適當的時候觸發自動完成事件,並呼叫這個方法用於自動完成功能。

Objective-C中協定的概念與Java中介面的概念並不完全相同,即一個類可以在不聲明它符合某個協定的情況下,實現這個協定所包含的方法,也即實質上符合這個協定,而這種差別對外部代碼而言是不可見的。正式協定的聲明不提供實現,它只是簡單地表明符合該協定的類實現了該協定的方法,保證呼叫端可以安全調用方法。

語法

協議以關鍵字@protocol作為區段起始,@end結束,中間為方法列表。

@protocol Locking
- (void)lock;
- (void)unlock;
@end

這是一個協議的例子,多執行緒編程中經常要確保一份共享資源同時只有一個執行緒可以使用,會在使用前給該資源掛上鎖 ,以上即為一個表明有「鎖」的概念的協議,協議中有兩個方法,只有名稱但尚未實現。

下面的SomeClass宣稱他採納了Locking協議:

@interface SomeClass : SomeSuperClass <Locking>
@end

一旦SomeClass表明他採納了Locking協議,SomeClass就有義務實現Locking協定中的兩個方法。

@implementation SomeClass
- (void)lock {
  // 實現lock方法...
}
- (void)unlock {
  // 實現unlock方法...
}
@end

由於SomeClass已經確實遵從了Locking協議,故調用端可以安全的發送lock或unlock訊息給SomeClass實體變數,不需擔心他沒有辦法回應訊息。

外掛模組是另一個使用抽象定義的例子,可以在不關心外掛模組的實現的情況下定義其希望的行為。

動態型別[編輯]

類似於Smalltalk,Objective-C具備動態型別:即訊息可以傳送給任何物件實體,無論該物件實體的公開介面中有沒有對應的方法。對比於C++這種靜態類型的語言,編譯器會擋下對(void*)指針調用方法的行為。但在Objective-C中,你可以對id發送任何訊息(id很像void*,但是被嚴格限制只能使用在物件上),編譯器僅會發出「該物件可能無法回應訊息」的警告,程式可以通過編譯,而實際發生的事則取決於執行期該物件的真正形態,若該物件的確可以回應訊息,則依舊執行對應的方法。

一個物件收到訊息之後,他有三種處理訊息的可能手段,第一是回應該訊息並執行方法,若無法回應,則可以轉發訊息給其他物件,若以上兩者均無,就要處理無法回應而拋出的例外。只要進行三者之其一,該訊息就算完成任務而被丟棄。若對「nil」(空物件指標)傳送訊息,該訊息通常會被忽略,取決於編譯器選項可能會拋出例外。

雖然Objective-C具備動態類型的能力,但編譯期的靜態型別檢查依舊可以應用到變數上。以下三種聲明在執行時效力是完全相同的,但是三種聲明提供了一個比一個更明顯的類型資訊,附加的類型資訊讓編譯器在編譯時可以檢查變數類型,並對類型不符的變量提出警告。

下面三個方法,差異僅在於參數的形態:

- setMyValue:(id) foo;

id形態表示參數「foo」可以是任何類別的例項。

- setMyValue:(id <aProtocol>) foo;

id<aProtocol>表示「foo」可以是任何類別的例項,但必須採納「aProtocol」協定。

- setMyValue:(NSNumber*) foo;

該聲明表示「foo」必須是「NSNumber」的例項。

動態型別是一種強大的特性。在缺少泛型的靜態型別語言(如Java 5以前的版本)中實現容器類時,程式設計師需要寫一種針對通用類型物件的容器類,然後在通用類型和實際類型中不停的強制類型轉換。無論如何,類型轉換會破壞靜態類型,例如寫入一個「整數」而將其讀取為「字串」會產生執行時錯誤。這樣的問題被泛型解決,但容器類需要其內容物件的類型一致,而對於動態型別語言則完全沒有這方面的問題。

轉發[編輯]

Objective-C允許對一個物件傳送訊息,不管它是否能夠回應之。除了回應或丟棄訊息以外,物件也可以將訊息轉發到可以回應該訊息的物件。轉發可以用於簡化特定的設計模式,例如觀測器模式代理模式

Objective-C執行時在Object中定義了一對方法:

  • 轉發方法:
- (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

希望實現轉發的物件只需用新的方法覆蓋以上方法來定義其轉發行為。無需重寫回應方法performv::,由於該方法只是單純的對回應物件傳送訊息並傳遞參數。其中,SEL類型是Objective-C中訊息的類型。

例子[編輯]

這裡包括了一個演示轉發的基本概念的程式範例。

Forwarder.h
#import <objc/Object.h>

@interface Forwarder : Object
{
    id recipient; //该对象是我们希望转发到的对象。
}

@property (assign, nonatomic) id recipient;

@end
Forwarder.m
#import "Forwarder.h"

@implementation Forwarder

@synthesize recipient;

- (retval_t) forward: (SEL) sel : (arglist_t) args
{
    /*
     *检查转发对象是否响应该消息。
     *若转发对象不响应该消息,则不会转发,而产生一个错误。
     */
    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.
    /*
     *转发者不响应hello消息!该消息将被转发到转发对象。
     *(若转发对象响应该消息)
     */
    [forwarder hello];

    return 0;
}

註腳[編輯]

利用GCC編譯時,編譯器報告:

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

如前文所提到的,編譯器報告Forwarder類不回應hello訊息。在這種情況下,由於實現了轉發,可以忽略這個警告。 執行該程式產生如下輸出:

$ ./a.out
Recipient says hello!

類別 (Category)[編輯]

在Objective-C的設計中,一個主要的考慮即為大型代碼框架的維護。結構化編程的經驗顯示,改進代碼的一種主要方法即為將其分解為更小的片段。Objective-C借用並擴充功能了Smalltalk實現中的「分類」概念,用以幫助達到分解代碼的目的。[3]

一個分類可以將方法的實現分解進一系列分離的檔案。程式設計師可以將一組相關的方法放進一個分類,使程式更具可讀性。舉例來講,可以在字串類中增加一個名為「拼寫檢查」的分類,並將拼寫檢查的相關代碼放進這個分類中。

進一步的,分類中的方法是在執行時被加入類中的,這一特性允許程式設計師向現存的類中增加方法,而無需持有原有的代碼,或是重新編譯原有的類。例如若系統提供的字串類的實現中不包含拼寫檢查的功能,可以增加這樣的功能而無需更改原有的字串類的代碼。

在執行時,分類中的方法與類原有的方法並無區別,其代碼可以存取包括私有類別成員變數在內的所有成員變數。

若分類聲明了與類中原有方法同名的函式,則分類中的方法會被呼叫。因此分類不僅可以增加類的方法,也可以代替原有的方法。這個特性可以用於修正原有代碼中的錯誤,更可以從根本上改變程式中原有類的行為。若兩個分類中的方法同名,則被呼叫的方法是不可預測的。

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

C#與Visual Basic.NET語言以擴充功能函式的與不完全類的方式實現了類似的功能。Ruby與一些動態語言則以"monkey patch"的名字稱呼這種技術。

使用分類的例子[編輯]

這個例子建立了Integer類,其本身只定義了integer屬性,然後增加了兩個分類ArithmeticDisplay以擴充功能類的功能。雖然分類可以存取類的私有成員,但通常利用屬性的存取方法來存取是一種更好的做法,可以使得分類與原有類更加獨立。這是分類的一種典型應用—另外的應用是利用分類來替換原有類中的方法,雖然用分類而不是繼承來替換方法不被認為是一種好的做法。

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"

int
main(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檔案來實驗。程式仍然可以執行,這表明了允許動態的、按需的載入分類;若不需要某一分類提供的功能,可以簡單的不編譯之。

扮演[編輯]

Objective-C允許一個類在程式中完全取代另一個類,這種行為稱為前者「扮演」目標類。

注意:類的扮演在Mac OS X v10.5中被廢棄,在64位元執行時中不可用。

#import[編輯]

在C語言中,#include預處理指令總是使被包含的檔案內容被插入指令點。在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定義內部不能定義C++命名空間。
  • Objective-C類的成員變數不能包括不含預設建構函式和/或含有虛方法的C++類別物件,但使用C++類別指標並無如此限制(可以在 -init方法中對之進行初始化)。
  • C++「傳遞值」的特性不能用在Objective-C物件上,而只能傳遞其指標。
  • Objective-C聲明不能存在在C++模板聲明中,反之亦然。但Objective-C類型可以用在C++模板的參數中。
  • Objective-C和C++的錯誤處理語句不同,各自的語句只能處理各自的錯誤。
  • Objective-C錯誤使得C++物件被退出時,C++解構函式不會被呼叫。新的64位元執行時解決了這個問題。[4]

Objective-C 2.0[編輯]

在2006年7月蘋果全球開發者會議中,Apple宣布了「Objective-C 2.0」的發布,其增加了「現代的垃圾收集,語法改進[5],執行時效能改進[6],以及64位元支援」。2007年10月發布的Mac OS X v10.5中包含了Objective-C 2.0的編譯器。

垃圾收集[編輯]

Objective-C 2.0提供了一個可選的垃圾收集器。在回溯相容模式中,Objective-C執行時會將參照計數操作,例如「retain」與「release」變為無操作。當垃圾收集啟用時,所有的物件都是收集器的工作物件。普通的C指標可以以「__strong」修飾,標記指標指向的物件仍在使用中。被標記為「__weak」的指標不被計入收集器的計數中,並在物件被回收時覆寫為「nil」。iOS上的Objective-C 2.0實現中不包含垃圾收集器。垃圾收集器執行在一個低優先級的後台執行緒中,並可以在用戶動作時暫停,從而保持良好的用戶體驗。[7]

屬性[編輯]

Objective-C 2.0引入了新的語法以聲明變數為屬性,並包含一可選定義以配置存取方法的生成。屬性總是為公共的,其目的為提供外部類存取(也可能為唯讀)類的內部變數的方法。屬性可以被聲明為「readonly」,即唯讀的,也可以提供儲存方法包括「assign」,「copy」或「retain」(簡單的賦值、複製或增加1參照計數)。預設的屬性是原子的,即在存取時會加鎖以避免多執行緒同時存取同一物件,也可以將屬性聲明為「nonatomic」(非原子的),避免產生鎖。

@interface Person : NSObject {
    @public
        NSString *name;
    @private
        int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

-(id)initWithAge:(int)age;
@end

屬性的存取方法由@synthesize關鍵字來實現,它由屬性的聲明自動的產生一對存取方法。另外,也可以選擇使用@dynamic關鍵字表明存取方法會由程式設計師手工提供。

@implementation Person
@synthesize name;
@dynamic age;

-(id)initWithAge:(int)initAge
{
    age = initAge; // 注意:直接赋给成员变量,而非属性
    return self;
}

-(int)age
{
    return 29; // 注意:并非返回真正的年龄
}
@end

屬性可以利用傳統的訊息運算式、點運算式或"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」關鍵字:

-(void) introduceMyselfWithProperties:(BOOL)useGetter
{
    NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name)); // NOTE: getter vs. ivar access
}

類或協定的屬性可以被動態的讀取。

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中,以下迴圈的功能是相等的,但效能特性不同。

// 使用NSEnumerator
NSEnumerator *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]);
}

快速列舉可以比標準列舉產生更有效的代碼,由於列舉所呼叫的方法被使用NSFastEnumeration協定提供的指標算術運算所代替了。[8]

語言分析[編輯]

Objective-C是非常「實際」的語言。它用一個很小的、用C寫成的執行庫,使得應用程式的大小增加很少,與此相比,大部分OO系統需要極大的執行時虛擬機器來執行。ObjC寫成的程式通常不會比其原始碼和函式庫(通常無需包含在軟體發行版本中)大太多,不會像Smalltalk系統,即使只是開啟一個窗口也需要大量的容量。由於Obj-C的動態型別特徵,Obj-C不能對方法進行行內(inline)一類的最佳化,使得Obj-C的應用程式一般比類似的C或C++程式更大。

Obj-C可以在現存C編譯器基礎上實現(在GCC中,Obj-C最初作為預處理器引入,後來作為模組存在),而不需要編寫一個全新的編譯器。這個特性使得Obj-C能利用大量現存的C代碼、庫、工具和編程思想等資源。現存C庫可以用Obj-C包裝器來提供一個Obj-C使用的OO風格介面包裝。

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

Objective-C的最初版本並不支援垃圾回收(garbage collection)。在當時這是爭論的焦點之一,很多人考慮到Smalltalk回收時有漫長的「死亡時間」,令整個系統失去功用,Objective-C為避免此問題才不擁有這個功能。某些第三方版本加入了這個功能(尤是GNUstep),蘋果公司也在其Mac OS X 10.5中提供了實現。

另一個廣受批評的問題是ObjC不包括命名空間機制(namespace mechanism)。取而代之的是程式設計師必須在其類別名稱加上前綴,由於字首往往較短(相比命名空間),這時常引致衝突。在2007年,在Cocoa編程環境中,所有Mac OS X類別和函式均有「NS」作為前綴,例如NSObject或NSButton來清楚分辨它們屬於Mac OS X核心;使用「NS」是由於這些類別的名稱在NeXTSTEP開發時定下。

雖然Objective-C是C的嚴格母集,但它也不視C的基本型別為第一級的物件。

和C++不同,Objective-C不支援運算子多載(它不支援ad-hoc多型)。亦與C++不同,但和Java相同,Objective-C只容許物件繼承一個類別(不設多重繼承)。Categories和protocols不但可以提供很多多重繼承的好處,而且沒有很多缺點,例如額外執行時間過重和二進位不相容。[來源請求]

由於Obj-C使用動態執行時類型,而且所有的方法都是函式呼叫(有時甚至連系統呼叫(syscalls)也如此),很多常見的編譯時效能最佳化方法都不能應用於Obj-C(例如:行內函式常數傳播互動式最佳化純量取代與聚集等)。這使得Obj-C效能劣於類似的物件抽象語言(如C++)。不過Obj-C擁護者認為Obj-C本就不應應用於C++或Java常見的底層抽象,Obj-C的應用方向是對效能要求不大的應用

參考文獻[編輯]

  1. ^ Mac OS X 10.6 Snow Leopard: the Ars Technica review, page 5
  2. ^ Adam Treat, mkspecs and patches for LLVM compile of Qt4 網際網路檔案館存檔,存檔日期2011-10-04.
  3. ^ Example of categories concept
  4. ^ Using C++ With Objective-C in Mac OS X Reference Library, last retrieved in 2010-02-10.
  5. ^ Objective-C 2.0: more clues. Lists.apple.com. 2006-08-10 [2010-05-30]. 
  6. ^ Re: Objective-C 2.0. Lists.apple.com. [2010-05-30]. 
  7. ^ Apple Computer, Inc. Leopard Technology Series for Developers: Objective-C 2.0 Overview. Developer.apple.com. 2007-11-06 [2010-05-30]. (原始內容存檔於2010-07-24). 
  8. ^ Apple, Inc. Fast Enumeration. apple.com. 2009 [2009-12-31]. 

外部連結[編輯]