-
object-c
鎖定
- 中文名
- Objective-C
- 別 名
- Objective-C
- 發明者
- 布萊德·確斯
- 適 用
- IOS開發
目錄
- 1 發展歷程及變化
- 2 語言性能與分析
- 3 語法
- ▪ Hello World
- ▪ 消息傳遞
- ▪ 類的定義與實現
- ▪ 協議(Protocol)
- ▪ 動態類型
- ▪ 轉發
- ▪ 類別 (Category)
- 4 語言變化
- ▪ Objective-C++
- ▪ Objective-C 2.0
- ▪ Modern Objective-C
- 5 語言分析
- 6 爭議
- 7 優點及缺點
- 8 和C++的比較
- 9 擴展的關鍵字
- ▪ @interface
- ▪ @implementation
- ▪ new、alloc
object-c發展歷程及變化
1980年初,布萊德·確斯( Brad Cox)發明了Objective-C,它是基於Smalltalk-80而發展起來的。Objective-C建立在C語言之上,意味着它是在C語言基礎上添加了擴展而創造出來的能夠創建和操作對象的一門新的程序設計語言。對Objective-C最主要的描述是1986年出版的Object-orientedProgramming,An Evolutionary Approach.
1988年,NeXT Sofiware公司開發出了Objective-C的語言庫,並且開發了一個名為NEXTSTEP的開發環境。
1996年,蘋果公司收購了NeXT Sofiware公司,然後把NEXTSTEP/OPENSTEP環境變成蘋果操作系統下一個主要發行版本OS X的基礎,這個開發環境的該版本被蘋果公司稱為Cocoa。
2006年,蘋果公司發佈了Objective-C 2.0,該版本增加了現代的垃圾收集語法改進、運行時性能改進和64位支持等功能。
Objective-C是GCC的一個前端,它可以編譯混合了C++與Objective-C語法的源文件。
Objective-C是C的擴展。下面列出了兩者的區別:
·Objective-C不支持多重繼承(同Java和Smalltalk),而C++支持多重繼承。
·C++類不能從Objective-C類繼承,反之亦然。
·Objective-C是動態定型(Dynamicaly Typed),所以它的類庫比C++要容易操作。Objective-C在運行時可以允許根據字符串名字來訪問方法和類,還可以動態連接和添加類。
·C++跟從面向對象編程裏的Simula 67( -種早期00語言)學派,而Objecive-C屬於Smalltalk學派。
·在C++裏,對象的靜態類型決定是否可以發送消息給它;而對Obj ective-C來説,由動態類型來決定。
·Objective-C定義內部不能定義C++命名空間。
·Objective-C類的成員變量不能包括不含默認構造函數和/或含有虛方法的C++類對象,但使用CH-類指針並無此限制(可以在-init方法中對其進行初始化)。
·C++“傳遞值”的特性不能用在Objective-C對象上,而只能傳遞其指針。
·Objective-C聲明不能存在C++模板聲明中,反之亦然,但Objective-C類型可以用在C++模板的參數中。
·Objective-C和C++的錯誤處理語句不同,各自的語句只能處理各自的錯誤。
·Objective-C錯誤使得C++對象被退出時,C++析構函數不會被調用。新的64位運行時解決了這個問題。
object-c語言性能與分析
Objective-C是非常“實際”的語言。它用一個很小的、用C寫成的運行庫,使得應用程序的大小增加很少,與此相比,大部分OO系統需要極大的虛擬機來執行。Objective-C寫成的程序通常不會比其源代碼和函式庫(通常無須包含在軟件發行版本中)大太多,不會像Smalltalk系統,即使只是打開一個窗口也需要佔用極大的內存。由於Objective-C的動態類型特徵,Objective-C不能對方法進行內聯(inline)一類的優化,使得Objective.C的應用程序一般比類似的C或C++程序更小。
Objective-C較強的編譯器適應性。Objective-C可以在現存C編譯器基礎上實現,而不需要編寫一個全新的編譯器。這個特性使得Objective-C能利用大量現存的C代碼、庫、工具和編程思想等資源。現存C庫可以用Obj ective-C包裝器來提供一個Objective-C使用的00風格界面包裝,這些特性極大地降低了進入Objective-C的門檻。
Objective-C垃圾回收機制的引進。最初Objective-C不支持垃圾回收機制。在Apple發佈的xCode4中已經支持自動釋放功能,嚴格而言,應該稱為“垃圾回收”。因為兩者機制不同,在xCode4中的自動釋放,也就是ARC (Automatic Reference Counting)機制,是不需要用户手動去釋放一個對象的,而是在編譯期間,編譯器會自動幫助人們添加那些以前經常寫的[NSObject release]。蘋果公司在其Mac OS X 10.5中也提供了這種實現。
Objective-C新的命名規則應用。Objective-C不包括命名空間機制,處理這個問題的方法是程序設計師必須在其類別名稱加上前綴,由於前綴往往較短(相比命名空間),這時常引致衝突。2007年,在Cocoa編程環境中,所有Mac OS X類別和函式均有NS作為前綴,如NSObject或NSButton來清楚分別它們屬於Mac OS X核心;使用NS是由於這些類別的名稱是在NeXTSTEP開發時定下的。
Objective-C獨有個性。雖然Objective-C是C的嚴格超集,但它也不視C的基本類型為第一級的對象。和C++不同,Objective-C不支援運算子多載(它不支持ad-hoc多型)。亦與C++不同,但和Java相同,Objective-C只容許物件繼承一個類別(不設多重繼承)。Categories和protocols不但可以提供很多多重繼承的好處,而且沒有很多缺點,如額外執行時間過重和二進制不兼容。
由於Objective-C使用動態運行時類型,而且所有的方法都是函數調用(有時甚至連繫統調用如syscalls也如此),很多常見的編譯時的性能優化方法都不能應用於Obj ective-C(例如,內聯函數、常數傳播、交互式優化、純量取代與聚集等),這使得Objective-C性能劣於類似的對象抽象語言(如CH)。不過Objective-C的擁護者認為既然Objective-C運行時消耗較大,Objective-C不該應用於C++或Java常見的底層抽象。
[1]
object-c語法
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。
object-cHello World
這裏示範了一個基礎的Hello World程序。基於Xcode 4.3.1:
#import <Foundation/Foundation.h>int main(int argc, char *argv[]) { @autoreleasepool { NSLog(@"Hello World!"); } return 0;}
object-c消息傳遞
Objective-C最大的特色是承自Smalltalk的消息傳遞模型(message passing),此機制與今日C++式之主流風格差異甚大。Objective-C裏,與其説對象互相調用方法,不如説對象之間互相傳遞消息更為精確。此二種風格的主要差異在於調用方法/消息傳遞這個動作。C++裏類別與方法的關係嚴格清楚,一個方法必定屬於一個類別,而且在編譯時(compile time)就已經緊密綁定,不可能調用一個不存在類別裏的方法。但在Objective-C,類別與消息的關係比較鬆散,調用方法視為對對象發送消息,所有方法都被視為對消息的迴應。所有消息處理直到運行時(runtime)才會動態決定,並交由類別自行決定如何處理收到的消息。也就是説,一個類別不保證一定會迴應收到的消息,如果類別收到了一個無法處理的消息,程序只會拋出異常,不會出錯或崩潰。
C++裏,送一個消息給對象(或者説調用一個方法)的語法如下:
obj.method(argument);
Objective-C則寫成:
[obj method: argument];
此二者並不僅僅是語法上的差異,還有基本行為上的不同。
此二種風格各有優劣。C++強制要求所有的方法都必須有對應的動作,且編譯期綁定使得函數調用非常快速。缺點是僅能借由virtual關鍵字提供有限的動態綁定能力。Objective-C天生即具備鴨子類型之動態綁定能力,因為運行期才處理消息,允許發送未知消息給對象。可以送消息給整個對象集合而不需要一一檢查每個對象的類型,也具備消息轉送機制。同時空對象nil接受消息後默認為不做事,所以送消息給nil也不用擔心程序崩潰。
object-c類的定義與實現
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;}
object-c協議(Protocol)
協議是一組沒有實現的方法列表,任何的類均可採納協議並具體實現這組方法。
非正式協議為一個可以選擇性實現的一系列方法列表。非正式協議雖名為協議,但實際上是掛於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實體變量,不需擔心他沒有辦法迴應消息。
插件是另一個使用抽象定義的例子,可以在不關心插件的實現的情況下定義其希望的行為。
object-c動態類型
類似於Smalltalk,Objective-C具備動態類型:即消息可以發送給任何對象實體,無論該對象實體的公開接口中有沒有對應的方法。對比於C++這種靜態類型的語言,編譯器會擋下對(void*)指針調用方法的行為。但在Objective-C中,你可以對id發送任何消息(id很像void*,但是被嚴格限制只能使用在對象上),編譯器僅會發出“該對象可能無法迴應消息”的警告,程序可以通過編譯,而實際發生的事則取決於運行期該對象的真正形態,若該對象的確可以迴應消息,則依舊運行對應的方法。
一個對象收到消息之後,他有三種處理消息的可能手段,第一是迴應該消息並運行方法,若無法迴應,則可以轉發消息給其他對象,若以上兩者均無,就要處理無法迴應而拋出的例外。只要進行三者之其一,該消息就算完成任務而被丟棄。若對“nil”(空對象指針)發送消息,該消息通常會被忽略,取決於編譯器選項可能會拋出例外。
雖然Objective-C具備動態類型的能力,但編譯期的靜態類型檢查依舊可以應用到變量上。以下三種聲明在運行時效力是完全相同的,但是三種聲明提供了一個比一個更明顯的類型信息,附加的類型信息讓編譯器在編譯時可以檢查變量類型,並對類型不符的變量提出警告
[2]
。
下面三個方法,差異僅在於參數的形態:
- setMyValue:(id) foo;
id形態表示參數“foo”可以是任何類的實例。
- setMyValue:(id <aProtocol>) foo;
id<aProtocol>表示“foo”可以是任何類的實例,但必須採納“aProtocol”協議。
- setMyValue:(NSNumber*) foo;
該聲明表示“foo”必須是“NSNumber”的實例。
動態類型是一種強大的特性。在缺少泛型的靜態類型語言(如Java 5以前的版本)中實現容器類時,程序員需要寫一種針對通用類型對象的容器類,然後在通用類型和實際類型中不停的強制類型轉換。無論如何,類型轉換會破壞靜態類型,例如寫入一個“整數”而將其讀取為“字符串”會產生運行時錯誤。這樣的問題被泛型解決,但容器類需要其內容對象的類型一致,而對於動態類型語言則完全沒有這方面的問題。
object-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中消息的類型。
object-c類別 (Category)
在Objective-C的設計中,一個主要的考慮即為大型代碼框架的維護。結構化編程的經驗顯示,改進代碼的一種主要方法即為將其分解為更小的片段。Objective-C借用並擴展了Smalltalk實現中的“分類”概念,用以幫助達到分解代碼的目的。
一個分類可以將方法的實現分解進一系列分離的文件。程序員可以將一組相關的方法放進一個分類,使程序更具可讀性。舉例來講,可以在字符串類中增加一個名為“拼寫檢查”的分類,並將拼寫檢查的相關代碼放進這個分類中。
進一步的,分類中的方法是在運行時被加入類中的,這一特性允許程序員向現存的類中增加方法,而無需持有原有的代碼,或是重新編譯原有的類。例如若系統提供的字符串類的實現中不包含拼寫檢查的功能,可以增加這樣的功能而無需更改原有的字符串類的代碼。
在運行時,分類中的方法與類原有的方法並無區別,其代碼可以訪問包括私有類成員變量在內的所有成員變量。
若分類聲明瞭與類中原有方法同名的函數,則分類中的方法會被調用。因此分類不僅可以增加類的方法,也可以代替原有的方法。這個特性可以用於修正原有代碼中的錯誤,更可以從根本上改變程序中原有類的行為。若兩個分類中的方法同名,則被調用的方法是不可預測的。
使用分類的例子
這個例子創建了Integer類,其本身只定義了integer屬性,然後增加了兩個分類Arithmetic與Display以擴展類的功能。雖然分類可以訪問類的私有成員,但通常利用屬性的訪問方法來訪問是一種更好的做法,可以使得分類與原有類更加獨立。這是分類的一種典型應用—另外的應用是利用分類來替換原有類中的方法,雖然用分類而不是繼承來替換方法不被認為是一種好的做法。
object-c語言變化
object-cObjective-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位運行時解決了這個問題。
object-cObjective-C 2.0
在2006年7月蘋果全球開發者會議中,Apple宣佈了“Objective-C 2.0”的發佈,其增加了“現代的垃圾收集,語法改進,運行時性能改進,以及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實現中不包含垃圾收集器。垃圾收集器運行在一個低優先級的後台線程中,並可以在用户動作時暫停,從而保持良好的用户體驗。
屬性
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
object-cModern Objective-C
蘋果公司在 WWDC2012 大會上介紹了大量 Objective-C 的新特性,能夠讓程序員更加高效地編寫代碼,這些新特性在 Xcode4.4 版本中已經可以使用。
- Object Literals
- 默認生成 @synthesize 代碼
- 遍歷元素
- Subscripting Methods
object-c語言分析
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在推廣中遇到的最大問題。
object-c爭議
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的應用方向是對性能要求不大的應用
object-c優點及缺點
Objective-C是非常“實際”的語言。它使用一個用C寫成、很小的運行庫,只會令應用程序的大小增加很小,和大部分OO系統使用極大的VM執行時間會取代了整個系統的運作相反,ObjC寫成的程序通常不會比其原始碼大很多。而其庫函數(通常沒附在軟件發行本)亦和Smalltalk系統要使用極大的內存來開啓一個窗口的情況相反。 Objective-C的最初版本並不支持垃圾回收。在當時這是爭論的焦點之一,很多人考慮到Smalltalk回收時有漫長的“死亡時間”,令整個系統失去作用。Objective-C為避免此問題才不擁有這個功能。雖然某些第三方版本已加入這個功能(尤是GNUstep),Apple在其Mac OS X 10.3中仍未引入這個功能。 另一個問題是ObjC不包括命名空間機制(namespace mechanism)。取而代之的是程序設計師必須在其類別名稱加上前綴,時常引致衝突。在2004年,在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不但可以提供很多多重繼承的好處,而且沒有很多缺點,例如額外執行時間過重和二進制不兼容。
object-c和C++的比較
- Objective-C是iOS的編譯語言。
- 與C++的不同之處有:
- OC中所有的類都必須繼承自NSObject。
- OC中所有對象都是指針的形式。
- OC用self代替this。
- OC使用id代替void*。
- OC使用nil表示NULL
- OC只支持單繼承。
- OC使用YES/NO表示TRUE/FALSE
- OC使用#import代替#include
- OC中用消息表示類的方法,並採用[aInstance method:argv]調用形式。
- OC支持反射機制
- OC支持Dynamic Typing, Dynamic Binding和Dynamic Loading。
- 與C++的相同之處有:
- 與C共享的部分一致。
- 可以使用assert(BOOL), 一般用NSCParameterAssert(BOOL)代替。
- OC中的命名前綴説明:
- NS-:NextStep
- CF-:Core Foundation
- CA-:Core Animation
- CG-:Core Graphics
- UI-:User Interface
- OC中的消息特殊性:
- 調用消息的類可以不知道如何響應這個消息。如果它不知道如何處理這個消息,它會自動的將這個消息轉給其他的類,比如它的父類。
- 調用消息的類可以是nil。在C++中,在使用類方法之前,我們都需要檢查對象是否為空,所以在實現析構函數的時候,常會有如下的代碼,如if (var) { delete var; }但是在objective c中,我們就可以直接寫[var release];即使var == nil, 也不會有問題。
- OC中的函數聲明格式有:
- -/+ (return type) function_name;
- -/+ (return type) function_name : (parameter type) parameter;
- -/+ (return type) function_name : (parameter type) parameter1otherParameter: (parameter_type) parameter2
- 以上參數説明:-表示一般函數,+表示靜態函數。otherParameter是參數的別名(第一個參數的別名省略),在函數調用時方便指定。
- OC中的構造/析構函數
- OC中的init()/release()對應於C++的構造/析構函數。alloc()/dealloc()也就對應於C++的new和delete,其中的dealloc()由於引用計數的自動調用而不用手動調用。
- OC中父類的init()/release()函數需要子類的手動調用。而且每次都必須調用。不同於C++的自動調用。
- 構造函數(- (id) init)調用形如:CSample* pSample=[[CSample alloc] init];其中alloc(+ (id) alloc)是繼承來的static函數,init是繼承來的一般函數,如重寫一般函數時,則相當於C++的覆蓋(不帶參數)或重載(帶參數)。
- 析構函數(- (void) release)將引用計數減1,當=0時父類的release()會自動調用dealloc(- (void) dealloc);
- 當OC沒有數據成員時,可省略{},建議保留。
- 繼承下來的方法,如:-(id) init可以頭文件中省略,建議保留
- OC中只有數據成員的訪問限制,沒有方法的訪問限制。
- 同C++一樣,數據成員有三種訪問限制public, protected, private,缺省是protected。
- 示例:@interface AccessExample: NSObject { @public int publicVar; @protected int protectedVar; @private int privateVar; } @end
- 方法的訪問限制可通過Category實現
- 示例:@interface MyClass- (void) sayHello {NSLog(@"Hello"); } @end @interface MyClass(Private)- (void) kissGoodbye;@end
- OC中沒有類的靜態變量,只有全局變量
- OC中的數組NSArray可以保存不同對象類型的數據。
- OC也支持runtime時的類型檢查
- - (BOOL) isKindOfClass: classObj 用於判斷該對象是否屬於某個類或者它的子類
- - (BOOL) isMemberOfClass: classObj 用於判斷該對象是否屬於某個類(這裏不包括子類)
- - (BOOL) respondsToSelector: selector 用於判斷該對象是否能響應某個消息。這裏,我們可以將@selector後面帶的參數理解為C++中的函數指針。 注意:1)不要忘了@ ;2)@selector後面用的是(),而不是[ ] ;3)要在消息名稱後面跟:,無論這個消息是否帶參數。如:[pSquare respondsToSelector:@selector(Set: andHeight:)]。
- + (BOOL) instancesRespondToSelector: selector 用於判斷該類的對象是否能響應某個消息。這是一個靜態函數。
- -(id) performSelector: selector :調用對象的selector方法。
- conformsToProtocol 類似於respondsToSelector ,用於動態檢查某個對象是否遵守某個協議。
- Category:在沒有源代碼的情況下,為一個已經存在的類添加一些新的功能
- 只能添加新的方法,不能添加新的數據成員
- Category的名字必須是唯一的
- Protocol:相當於C++中的純虛類
- 形如:@interface MyDate: NSObject <Printing> { } @end
- 使用:MyDate * dat = [[MyDate alloc] init];id<Printing> var = dat; [var print]。
- 説明:我們首先聲明瞭Printing 協議,任何遵守這個協議的類,都必須實現print 方法。在Objective C 中,我們通過<>來表示遵守某個協議。當某個類聲明要遵守某個協議之後,它就必須在.m文件中實現這個協議中的所有方法。使用id<Printing> 作為類型,而不是象C++中的Printing* var。
- IBOutlet, IBAction: 這兩個東西其實在語法中沒有太大的作用。如果你希望在Interface Builder中能看到這個控件對象,那麼在定義的時候前面加上IBOutlet,在IB裏就能看到這個對象的outlet,如果你希望在Interface Builder裏控制某個對象執行某些動作,就在方法前面加上(IBAction)。
- 儘量避免在一行語句中進行兩層以上的嵌套
- 消息轉發:- (void) forwardInvocation: (NSInvocation*)anInvocation;
object-c擴展的關鍵字
object-c@interface
類型聲明,類似於c++中的class,區別在於Object c中的聲明與實現是強制分開的,@interface關鍵字用於類型的聲明,包括數據成員、方法聲明、屬性等。方法的參數傳遞採用中綴符的形式,利用“:”分割參數名和被傳遞參數,類型的聲明以@interface開頭,以@end結束,通常一個類型的聲明採用下面的結構:
@class someOtherObject //外部類型聲明
@interfacesomeObject:NSObject //繼承的類型
{
int i; //成員變量
}
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg; //對象的方法
+(id)someMethod:(int)someArg; //類方法
-(id)init; //初始化方法
@propertyint num; //屬性
@end
object-c@implementation
對應於@interface的類型聲明,@implementation表示一個類型的實現過程,同樣以@end結束,實現的格式通常如下:
@implementationsomeObject
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg
{
//實現代碼
}
@synthesize num=i; //將屬性與變量進行對應
@end
object-cnew、alloc
Object C中的方法調用形式採用消息發送的方式,通常調用的形式如
[someObject someMethod:firstArg someOtherArgName:otherArg]
實例的初始化也採用消息發送的形式,可以簡單的調用類型的new方法來獲取一個實例對象,簡單實例化的方法通常是:
someObject *obj = [someObjectnew]; //類的實例化
new方法的實際過程是調用alloc和init方法,因此如果需要採用自定義的方法來初始化實例,則需要自己重寫init方法,通常的初始化方式為:
someObject *obj = [[someObject alloc] init]; //採用無參數的init實例化
someObject *obj = [[someObject alloc] initWithArg:Arg]; //採用參數的實例化
object-c@class
@class是一個前向引用聲明,類似於C++中的friend友元聲明,其作用是告訴編譯器其後面的字段代表一個類型名稱,儘管不知道類型的具體實現,但是隻需要將其作為一個類型名稱處理即可。通常在使用複合的結構時可以採用@class來減少頭文件的相互引用,如果出現循環依賴,則需要依靠@class來避免引用的死循環。通常使用形式為:
@classsomeOtherObject;
@interfacesomeObject:NSObject
{
someOtherObject *obj;
}
@end
object-c@property
儘管可以使用obj->arr的形式去強制讀取對象的成員變量,但是良好的編程形式是對外界提供成員變量的讀寫接口。@property關鍵字提供了外界對成員變量的訪問接口,其本質是為某一個屬性提供set和get操作。根據不同的需要,可以添加readonly(只讀,相當於只添加get不添加set方法)或者readwrite(讀寫,如果不添加則為默認);還有三種賦值方式可選:assign(直接賦值,通常用於基本類型),retain(釋放舊值,增加新的retaincount),copy(常用於字符串,生成一個新的拷貝)。通常使用的方式如下:
@interface someObject:NSObject
{
int i; //成員變量
}
@property (assign,readonly) int num; //屬性
@end
object-c@synthesize
與@property對應,將一個外在屬性與成員變量相關聯,定義在@implementation中,如果屬性名與變量名一致則可以省略變量名。常用方法:
@implementation someObject
@synthesize num=i;//如果屬性名也為i,則可以直接寫為 @synthesizei
@end
object-c內存管理
Object C採用引用計數的方式進行內存管理,由於所有的對象都繼承於NSObject,因此所有的對象都可以接受NSObject的三個方法:
-(id)retain;
-(void)release;
-(unsigned)retainCount;
retain方法將對象的引用計數加一併返回該對象,release將引用計數減一,retainCount方法返回對象當前的引用計數。當採用new、alloc、copy方法創建一個對象時,它的引用計數被置為1,如果程序中對該對象進行操作,則應根據需要,通過調用retain和release方法來保證該對象在不需要的時候被清除。當一個對象的引用計數被置為0後,系統會自動向對象發送一個dealloc消息,將其佔有的資源釋放。通常情況下,如果一個對象的初始化過程調用了其他資源,則應該重寫改對象的dealloc過程,保證在對象的銷燬期正確釋放這些資源。
為了更加方便的進行能存管理,cocoa中提供了一個自動釋放池(autorelease pool)的概念,每一個類都繼承了一個autorelease方法,當調用對象的autorelease方法時,改對象會被加入到開始創建的自動釋放池中。當程序進行到不再需要自動釋放池中的對象時,將自動釋放池釋放的時候會向池中的所有對象發送一個release消息,從而保證不再需要的對象被正確的釋放。通常的用法如下:
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
someObject * obj = [[someObject alloc] init];
[obj autorelease]; //加入自動釋放池
//其他代碼
[pool release]; //執行該語句時,系統會向池內所有的對象發送release消息;在這個例子中,如果對obj進行的其他retain操作和release操作保持一致的話,則會將obj的引用計數變為0,從而調用它的dealloc方法進行資源釋放
Object C進行內存管理的3條規則是:
- 如果使用new、alloc或copy操作獲得一個對象,則該對象的保留計數器值為1
- 如果通過任何其他方法獲得一個對象,則假設該對象的保留計數器值為1,而且已經被設置為自動釋放
- 如果保留了某個對象,則必須保持retain方法和release方法的使用次數相等。
object-c類別
類別是為現有的類提供一個新的方法的方法,即使沒有一個類的源代碼,仍然可以向類中添加一個方法以方便使用。類別的主要目的有3個:將類的實現分散到多個不同的文件或框架中,創建對私有方法的前向引用,向對象添加非正式協議。
類別的聲明方法:
@interfacesomeObject (someProtocal)
-(void)someMethod;
@end
類別的實現方法:
@implementationsomeObject(someProtocal)
-(void)someMethod
{
}
@end
object-c@protocol
Object C中的協議類似於java中的接口,通過@protocol關鍵字定義一個或多個需要遵從協議的對象實現的方法,協議定義的方法:
@protocolsomeProtocol
-(void)someMethod;
@end
採用協議的方法是在類聲明時使用尖括號註明其需要使用的協議:
@interfacesomeObject:NSObject <someProtocol>
在類實現時需要將協議中規定的方法都予以實現。
Object C 2.0增加了2個新的協議修飾符@optional和@required,可以規定協議中的方法是否為必須實現的方法。