本文將討論 Objective-C 語言的核心語法。這部分開始詳述一些具體的語法。正如你期待的同樣,涉及到了定義和類。程序員
在Smalltalk中,類是具備一些特性的對象。在Objective-C中也同樣。一個類是一個對象,對象迴應消息。Objective-C和C++都分離了對象分配和初始化。數組
在C++中,對象分配經過新的操做。在Objective-C中,這樣的操做是經過給類發送分配消息—調用malloc()或者一個等價。框架
C++中的初始化是經過調用一個與類同名的函數。Objective-C並無區別初始化方法和其餘方法,但出於慣例缺省的初始化方法就是初始化。函數
當你聲明一個方法讓實例去迴應,聲明一般已「-」開頭,而且「+」用做類的方法。在文檔中對這些消息使用一些前綴是很廣泛的,因此你也能夠說+alloc和-init來暗示alloc是傳給一個類,init傳給實例。佈局
類在Objective-C中,正如在其餘一些面嚮對象語言,都是對象工廠。大多數類不用自行實現+alloc,而是從他們的父類中繼承。在NSObject中,父類在大多數Objective-C程序中,+alloc方法調用+allocWithZone:.使NSZone做爲一個參數,一個C結構包含對象分配的一些策略。回顧19世紀80年代,當Objective-C用在NeXTstep來實現設備驅動和只有8MB內存25MHZ的CPU機器的GUI上面時,NSZone對優化很是重要。同時,這或多或少的被Objective-C程序員所忽視。(頗有可能成爲象NUMA構架同樣流行,更廣泛。)性能
衆多優秀的特性之一就是對象建立語義是由庫定義的而且語言不是類簇的思想。當你傳一個-init消息給對象時,它返回一個初始化對象。這多是你發送消息的那個對象,但不必定確定就是。這和其餘初始化程序一致的。頗有可能一些公共類的特殊子類在不一樣數據上更有效。優化
實現這個特性的通用方法叫作isa-swizzling。正如我前述,Objective-C對象是C結構,這些結構第一個元素是指向類的指針。這個元素是可存取的,正如其餘實例變量同樣;你能夠在運行時經過分配新值來改變對象的類。固然,若是你對對象的類設置在內存中有着不一樣的佈局,這些設置可能嚴重錯誤。設計
然而,你能夠經過一個父類來定義佈局和經過子集的集合定義行爲,舉例來講,這個技術用在標準化字符串類(NSString),它對不一樣的文本字符集、靜態事物和其它一些有着各類各樣的實例。指針
由於類是對象,你能夠象操做對象同樣操做他們。舉例來講,你能夠把他們放在集合。當我有一些輸入事件須要經過不一樣的類的實例來處理時我就使用這種格式。你須要建立一個目錄映射事件命名到類,而後爲每個輸入事件實例化一個對象。若是你在一個庫中這麼作,它容許代碼的使用者輕鬆的註冊屬於他們本身的句柄。code
Objective-C沒有公開容許在堆棧上定義對象。但並非真的—頗有可能在堆棧上定義對象,但有些困難,由於它破壞了對內存管理的一種假設。結果,每個Objective-C對象都是一個指針。一些類型由Objective-C定義;這些類型在頭部定義做爲C類型。
在Objective-C中最廣泛的3種類型就是id,Class和SEL。id就是指向Objective-C對象的指針,它等價於C語言中的void*,你能夠映射任何對象指針類型指向它而且映射他指向其它的對象指針類型。
你能夠傳任何消息給id,但若是不支持的話會返回一個運行時異常。
類是指向Objective-C類的指針。類是對象,因此也能夠接收消息。類名是一種類型,不是可變的。標識符NSObject是一個NSObject實例的類型,但也可做爲消息接受者。你能夠得到一個類,以下:
[NSObject class];
發送一個+class消息給NSObject類,而後返回一個指向表明類的類結構指針。
這對咱們回顧是很是有用的[FS:PAGE],正如咱們在這個系列第二部分看到的同樣。
第三種類型SEL,表明一個選擇器—一個表明方法名的抽象。你能夠在編譯時經過@selector()直接建立,或在運行時經過C字符串調用運行時庫函數,或用OpenStep NSSelectorFromString()函數,這個函數給Objective-C字符串一個選擇器。這個技術容許你經過名字調用方法。你能夠在C中經過使用相似dlsym(),但在C++中很是不一樣。在Objective-C中,你能夠作的以下:
[object perfomSelector:@selector(doSomething)];
這等價於以下:
[object doSomething];
顯然,第二種格式速度稍微快些,由於第一種傳送兩個消息。後面,咱們會看到經過選擇器處理的一些細節。
C++沒有與id相同的類型。由於對象老是能夠類型化的。在Objective-C,你能夠選擇類型系統。下面的兩種都是有效的:
id object = @」a string」; NSString *string = @」a string」;
常量字符串其實是NSConstantString類的一個實例,NSConstantString類是NSString的子類。將它引用到NSString* 使編譯時對消息進行類型檢查和存儲公共實例變量(這在Objective-C從未使用過)。注意你能夠經過以下改變這一設置:
NSArray *array = (NSArray*)string;
若是給數組發送消息,編譯器將會檢查NSArray能接收的消息。這並非很是有用,由於對象是一個字符串。若是發送一個NSArray和NSString實現的消息,可能會有做用。若是你發送的消息NSString沒有實現,一個異常將會拋出。
強調Objective-C和C++的不一樣的這件事看起來比較奇怪。Objective-C有類型-值語法,而C++有類型-變量語法。在Objective-C,對象類型是對象專有的一種屬性。在C++,類型取決於變量的類型。
在C++中,當你指派一個指針指向一個對象到一個變量定義一個指向父類的指針,兩個指針可能沒有相同的數值(這能夠經過多繼承實現,而Objective-C不支持這種。)
Objective-C類定義有一個接口和一個實現部分。與C++有類似的地方,但兩個稍微有些混。
Objective-C中的接口僅定義位而且明確的須要公開。對於實現的緣由,這包括私有實例變量在大部分的實現中,由於你沒法繼承一個類除非你知道它多大。最近的一些實現,象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)。在括號內的名字是協議——與Java中的接口類似——經過類來實現。
正如C++實例變量(C++中的域)能夠存取修飾符,不象C++,這些修飾符以@爲前綴是爲了不與C標識符衝突。
Objective-C不支持多繼承,因此只有一個父類。因此,對象第一部分的佈局老是與父類實例的佈局一致。這在過去經常定義爲動態,意味着改變類中實例變量須要它全部子類從新編譯。在較新的運行時這種限定並不要求,在存取實例實例變量上開支稍微大些。這種決策的另外一個影響就是Objective-C其餘特性中的一個。
struct_AnObject { @defs(AnObject); };
@def表示着對特定對象全部域都插入這種結構,因此struct_AnObject 和AnObject類的實例有着相同的內存結構。舉個例子來講,你能夠經過這種規則能夠直接存取實例變量。一個一般的用法就是容許C函數直接操做Objective-C對象,是基於性能緣由。
正如我前面暗示的,與這個特性相關的另外一件事就是能夠在堆棧上建立對象。由於結構和對象在[FS:PAGE]內存佈局中有着相同的結構,你能夠簡單的建立結構,設置他的指針指向正確的類,而後映射一個指針指向一個對象指針。而後你能夠當作對象來使用,雖然你不得不當心沒有什麼能夠保持指針不越界。(現實世界中我從沒有使用這種方法,僅僅理論上可能。)
不象C++,Objective-C沒有私有的或受保護的方法。Objective-C對象上的任何方法能夠被其餘對象調用。若是你在接口中沒有聲明方法,就是非正式私有的。將會獲得運行時警告:對象不迴應這種消息,可是你任然能夠調用它。
接口和C中頭部聲明很類似。但它仍然須要一個實現,這並不奇怪,可使用@implementation來定義。
@implementation AnObject + (id) aClassMethod { ... } - (id) anInstanceMethod:(NSString*)aString with:(id)anObject { ... } @end
注意參數類型是特定的,在括號裏。這是從C重用映射語法來展現值映射到類型;他們可能不是類型。準確來講當映射時應用相同的規則。這意味着映射在不兼容對象指針類型間會致使一個警告(不是錯誤)。
傳統的,Objective-C不提供任何內存管理。在早期版本中,對象類實現一個+new方法調用malloc()來建立一個新對象。當使用完這個對象,傳一個-free消息。任何一個對象從NSObject繼承迴應一個-retain和-release消息。當你使用完這個對象,你傳一個-free消息。OpenStep添加了參考計算。
每個從NSObject繼承的對象都回應-retain和-release消息。當你想要保留一個指向對象的指針,你能夠發送一個-retain消息。當你使用完之後,你能夠發送一個-release消息。
這個設計有個細微問題。一般你不須要保持一個指向對象的指針,可是你也不想釋放。一個典型的例子在返回一個對象時候,調用者須要保持指向對象的指針,但你不想這麼作。
這個問題的解決方案就是NSAutoreleasePool類。加上-retain和-release,NSObject也迴應-autorelease消息。當你發送其中一個,和現前的自動釋放池一同註冊。當這個池對象被註銷,它發送一個-release消息給每一個對象,而對象在這以前先收到-autorelease消息。在OpenStep應用中,一個NSAutoreleasePool實例在循環開始的時候建立,在結束的時候銷燬。你也能夠建立屬於你本身的實例來自動釋放對象。
這個機制減小了一些C++所需的複製。其實也不值得這麼作,在Objective-C,易變性是對象的屬性,不是參考。在C++,有常量指針和很是量指針。不容許在常量對象上調用很是量方法。這保證不了對象不會被改變——僅僅由於你不想改變。
在Objective-C中,一個常態模式定義了一個不變的類和可變的子類。NSString就是一個典型例子;
它有一個可變的子類NSMutableString。若是你獲得NSString而且想保存下來,你能夠傳一個-retain消息而且不用複製操做就能夠保存指針。相反地,你能夠傳一個+stringWithString:message給NSString。無論這個參數是否可變都會檢查並返回原始指針。
在Apple和GNU運行時,Objective-C都支持存儲性的垃圾回收,這會避免對-retain和-release的須要。在現存的框架中對語言的附加並不老是很好的支持的,而且在用的時候須要格外當心。
via 伯樂在線