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]. 已忽略未知参数
|=
(帮助)