Objective-C
編程範型 | 面向對象 |
---|---|
設計者 | Tom Love和Brad Cox |
實作者 | Apple Inc. |
釋出時間 | 1986 |
目前版本 |
|
型態系統 | 弱型別,動態型別 |
作業系統 | Cross-platform |
網站 | developer |
主要實作產品 | |
Clang、GCC | |
啟發語言 | |
Smalltalk、C | |
影響語言 | |
Java、Ruby、TOM |
Objective-C,通常寫作ObjC和較少用的Objective C或Obj-C,是在C的基礎上,加入物件導向特性擴充而成的程式語言。
目前,Objective-C主要應用於Mac OS X和iOS這兩個NeXTSTEP的衍生系統,而在NeXTSTEP和OpenStep中它更是基本語言。Objective-C可以在任何gcc支援的平台上進行編譯,因為gcc原生支援Objective-C。
歷史
1980年代初,布萊德·確斯(Brad Cox)在其公司Stepstone發明Objective-C,它以一種叫做SmallTalk-80的語言為基礎。Objective-C建立在C語言之上,意味着它是在C語言基礎上添加了擴充而創造出來的能夠建立和運算物件的一門新的程式語言。對Objective-C最主要的描述是他1986年出版的《Object-oriented Programming, An Evolutionary Approach》。1988年,NeXT Software公司獲得了Objective-C語言的授權,並開發出了Objective-C的語言庫和一個名為NEXTSTEP的開發環境。1992年,自由軟件基金會的GNU開發環境增加了對Objective-C的支援。1994年,NeXT Computer公司和昇陽微系統(Sun Microsystem)聯合發佈了一個針對NEXTSTEP系統的標準典範,名為OPENSTEP。OPENSTEP在自由軟件基金會的實現名稱為GNUStep。1996年12月20日,蘋果公司宣佈收購NeXT Software公司,NEXTSTEP/OPENSTEP環境成為蘋果作業系統下一個主要發行版本OS X的基礎。這個開發環境的該版本被蘋果公司稱為Cocoa。
語法
Objective-C是C語言的嚴格母集合,意指任何原始的C語言程式都可以通過Objective-C編譯器而不需修改,也允許 Objective-C 使用任何原始的C語言代碼。Objective-C 形容自己為覆蓋於C語言上的一層薄紗,這是因為Objective-C的原意就是在原始C語言主體上加入物件導向的特性。Objective-C的物件導向語法源於Smalltalk訊息傳遞風格。所有其他非物件導向的語法,包括變數型別,前處理器(preprocessing),流程控制,函數宣告與呼叫皆與C語言完全一致。
Hello World
這里示範了一個基礎的Hello World程式。
#import<Foundation/Foundation.h>
int main(int argc, char *argv[]){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(@"Hello World!");
[pool drain];
return 0;
}
以上是Xcode的舊版"Hello World"程式碼,最新的4.2.1 xcode的代碼為:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]){
@autoreleasepool{
NSLog(@"Hello World!");
}
return 0;
}
訊息傳遞
Objective-C 物件導向最大的特色是的訊息傳遞(message passing)模型。Objective-C裏,物件不呼叫方法,而是互相傳遞訊息,這與今日的主流C++式物件導向風格差異甚大。此二種風格的差異主要在於程式如何看待呼叫方法/傳送訊息這個動作。C++裏類與方法的關係非常嚴格清楚,一個方法必定屬於一個類,且於編譯期(compile time)就已經緊密綁在一起,你不可能呼叫一個不存在類裏的方法。但在Objective-C,類與訊息的關係較為鬆散,所有方法都被視為對訊息的回應,而呼叫方法則視為對類傳送訊息。所有訊息處理直到執行期(runtime)才會動態決定,並交由類自行決定如何處理收到的訊息。也就是說,一個類不保證一定會回應收到的訊息,如果類收到了一個無法處理的訊息,程式並不會出錯或當掉,它只會拋出一個Exception。
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方法,這里不會產生編譯錯誤或執行時錯誤,它僅僅是什麼都不做,返回一個nil。
此二種風格各有優劣。C++的物件導向風格支援多重繼承,編譯期綁定使得函數呼叫非常快速,強制要求所有的方法都必須有對應的動作。缺點是不支援動態綁定(除非手動加上virtual關鍵字)。Objective-C允許傳送未知的訊息給物件,執行期才處理訊息。例如你可以送訊息給整個物件集合而不需要一一檢查每個物件的型態,甚至傳送訊息給nil也不用擔心造成程式崩潰。
Objective-C的方法呼叫因為執行期才動態解析訊息,一開始訊息比C++ virtual成員函數呼叫速度慢上三倍。但經由IMP快取改善,目前已經比C++的virtual function快上50%。
類的宣告與實現
Objective-C 類要求分割介面(interface)與實現(implementation)為兩個代碼區塊,這是強制性的。
通常類的介面會放置於標頭檔內,依C語言的慣例以.h作為副檔名;類的實現則放於代碼檔以.m為副檔名。
Interface
介面區段裏頭清楚定義了類的名稱,實體變數(instance variable),以及方法。 以關鍵字@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 {
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定義一個新的方法時,名稱內的冒號(:)代表參數傳遞,不同於其他語言以數學函數的括號來傳遞參數。這使得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
實做區段則撰寫方法的實際運行代碼。以關鍵字@implementation作為區段起頭,@end結尾。
@implementation MyObject
+(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
創建物件
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;
}
協定
Objective-C在NeXT時期曾經試圖引入多重繼承的概念,但由於協定的出現而沒有實現之。協定的功能類似於C++中對抽象基礎類別的多重繼承或是類似Java與C#中的「介面」。在Objective-C中,包括兩種定義協定的方式:為特定目的設定的「非正式協定」,以及由編譯器保證的「正式協定」。
非正式協定即為一個類可以選擇性實現的一系列方法的列表。由於它的定義並沒有在程式中出現,它的定義通常在文件中給出。非正式協定通常包含可選的方法,實現這些方法可以使某個類的行為改變。例如文本框類通常會包括一個委託對象,該對象可以實現一個非正式協定,該協定中可能包含一個可選的、用於實現用戶輸入的自動完成的方法。若這個委託對象實現了這個方法(通過反射,那麼文本框類就會在適當的時候呼叫這個方法用於支援自動完成功能。
正式協定則類似於Java中的"介面",它是一系列方法的列表,任何類都可以聲明自身實現了某一個或一些協定。在Objective-C 2.0之前,一個類必須實現它聲明符合的協定中的所有方法,否則編譯器會報告一個錯誤,表明這個類沒有實現它聲明符合的協定中的全部方法。Objective-C 2.0版本允許標記協定中某些方法為可選的,這樣編譯器就不會強制實現這些可選的方法。
Objective-C中協定的概念與Java中介面的概念有所不同,即一個類可以在不聲明它符合某個協定的情況下,實現這個協定所包含的方法,也即實質上符合這個協定,而這種差別對外部代碼而言是不可見的。正式協定的聲明不提供實現,它只是簡單的對呼叫者假定符合該協定的類實現了該協定的方法。
語法
@protocol Locking
- (void)lock;
- (void)unlock;
@end
表明有「鎖」的抽象觀念。以下聲明表明了這個類實現了協定Locking:
@interface SomeClass : SomeSuperClass <Locking>
@end
SomeClass的實例聲明它提供了Locking協定中的兩個方法的實現,無論其語意如何。外掛程式是另一個使用抽象定義的例子,可以在不關心外掛程式的實現的情況下定義其希望的行為。
動態型別
類似於Smalltalk,Objective-C支援動態型別:訊息可以傳送向任意的類別實例,而該實例的類型不一定要與定義相符。這種特性可以增加語言的靈活性,因為它允許對象「捕捉」訊息,再將訊息轉送到另一個可以正確處理該訊息的對象,或者將訊息「轉發」給另一個對象。這種行為被稱為「訊息轉發」或「委託」(見下文)。同時,在訊息無法被轉發的情況下,可能會產生一個錯誤。若對象不能響應、轉發訊息或產生錯誤,該訊息會被丟棄而不產生任何資訊。因此,若對「nil」(空對象指標)傳送訊息,該訊息會被忽略或產生錯誤,取決於編譯器選項。
靜態型別資訊也可以應用到變數上,這些資訊會在編譯期被檢查。以下三種聲明提供了一個比一個明顯的類型資訊。這三種聲明在執行時是等同的,但附加的類型資訊允許編譯器在編譯時檢查變數類型,並在類型不符的情況下提出警告。
- setMyValue:(id) foo;
該聲明表示「foo」可以是任何類別的實例。
- setMyValue:(id <aProtocol>) foo;
該聲明表示「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!
類別
在Objective-C的設計中,一個主要的考慮即為大型代碼框架的維護。結構化編程的經驗顯示,改進代碼的一種主要方法即為將其分解為更小的片段。Objective-C借用並擴充了Smalltalk實現中的「類別」概念,用以幫助達到分解代碼的目的。[3]
一個類別可以將方法的實現分解進一系列分離的檔案。程式設計師可以將一組相關的方法放進一個類別,使程式更具可讀性。舉例來講,可以在字串類中增加一個名為「拼寫檢查」的類別,並將拼寫檢查的相關代碼放進這個類別中。
進一步的,類別中的方法是在執行時被加入類中的,這一特性允許程式設計師向現存的類中增加方法,而無需持有原有的代碼,或是重新編譯原有的類。例如若系統提供的字串類的實現中不包含拼寫檢查的功能,可以增加這樣的功能而無需更改原有的字串類的代碼。
在執行時,類別中的方法與類原有的方法並無區別,其代碼可以存取包括私有類別成員變數在內的所有成員變數。
若類別聲明了與類中原有方法同名的函數,則類別中的方法會被呼叫。因此類別不僅可以增加類的方法,也可以代替原有的方法。這個特性可以用於修正原有代碼中的錯誤,更可以從根本上改變程式中原有類的行為。若兩個類別中的方法同名,則被呼叫的方法是不可預測的。
其它語言也嘗試了通過不同方法增加這一語言特性。TOM在這方面走的更遠,不僅允許增加方法,更允許增加成員變數。也有其它語言使用面向聲明的解決方案,其中最值得注意的是Self語言。
C#與Visual Basic.NET語言以擴充函數的與不完全類的方式實現了類似的功能。Ruby與一些動態語言則以"monkey patch"的名字稱呼這種技術。
使用類別的例子
這個例子建立了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"
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年蘋果全球開發者會議中,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執行時消耗較大,Obj-C本來就不應應用於C++或Java常見的底層抽象。
參考資料
- ^ https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html; 檢索日期: 2019年9月2日.
- ^ Mac OS X 10.6 Snow Leopard: the Ars Technica review, page 5
- ^ 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. 2009 [2009-12-31]. 已忽略未知參數
|=
(幫助)