前言
資料型別 (data types) 是用來界定資料在電腦程式中合法的操作,像是數字型別的資料可進行四則運算 (加減乘除) 但字串型別的資料則無法進行這些運算。本文介紹 Objective-C 中的資料型別。由於 Objective-C 衍生自 C 語言,原本 C 語言的資料型別在 Objective-C 仍可繼續沿用。
來自 C 語言的基礎型別
這些型別源自於 C 語言,在 C 語言或 Objective-C 中無法再化簡。包括以下型別:
- 布林數 (Boolean) (自 C99 後可用)
bool
- 字元 (Character)
- 帶號字元:
char
- 無號字元:
unsigned char
- 帶號字元:
- 整數 (Integer)
- 帶號整數:
short
、int
、long
、long long
- 無號整數:
unsigned short
、unsigned
、unsigned long
、unsigned long long
- 帶號整數:
- 浮點數 (Floating-point numbers)
- 單精度
float
- 倍精度
double
- 雙倍精度
long double
- 單精度
- 複數 (Complex number) (自 C99 後可用)
complex
long complex
- 列舉 (Enumration)
布林數 (Boolean)
原本的 C 語言中沒有布林數的概念,而是由程式設計師自行用巨集去定義常數,如下例:
#define TRUE 1
#define FALSE 0
在 C99 標準後,可以在 C 語言使用布林數,使用時需引入 stdbool.h 頭文件 (header)。布林數的型別為 _Bool
或其別名 bool
,有 true
和 false
兩種值。同樣也可以在 Objective-C 中使用。
Objective-C 中另外定義了 BOOL
型別,該型別其實是帶號字元的別名,有 YES
和 NO
兩種值。使用的概念也是當成布林數來用。
要注意 bool
和 BOOL
是兩種不同的型別,如果不是寫純 C 程式,用 Objective-C 定義的 BOOL
會比 C99 定義的 bool
來得好,因為可以和大部分的 Objective-C 程式碼接軌。
字串 (String)
C 語言沒有原生的字串型別,所謂的 C 字串其實是以 NULL 結尾的 char
型別陣列。所以 C 字串相對來說比較低階,因為程式設計師是直接操作陣列。
相較來說,Objective-C 提供 NSString
類別,雖然 NSString
內部其實也是 UTF-16 字元陣列,但提供了許多操作 NSString
的方法 (method),操作起來不會感到那麼低階。必要時,也可以將 NSString
物件和 C 字串互轉。
整數 (Integer)
C 語言的整數型別有很多種,這些型別差別在於資料的範圍大小和是否需帶正負號。如果不是在嵌入式系統等必需計較運算資源的場合,其實視情形用 int
或 unsigned
即可。每個系統上各個數字型別的上下限會隨實作而有差異,其極端值可見 limits.h,另外,stdint.h (自 C99 後可用) 提供大小固定的整數型別。
Objective-C 另外提供 NSNumber
類別,這個類別的用途是將各種數字型別資料包成物件。
浮點數 (Floating-Point Number)
C 語言的浮點數有三種型別,主要的差別在於資料大小。一般來說,會優先使用 double
;相較於 float
型別,double
型別可降低反覆運算所造成的誤差。
在 Objective-C 中,同樣也可以使用 NSNumber
包住數字型別資料。
複數 (Complex Number)
複數是 C99 後可用的數字資料型別,根據大小分為兩種型別。在 Objecitve-C 中可繼續使用。
列舉 (Enumeration)
列舉是由程式設計師自行定義的識別字,而這些符號共享相同的型別。使用列舉的意義在於使用這些符號 (symbols),通常不會注重列舉符號的實際值。以下是實例:
typedef enum {
TRAFFIC_LIGHT_RED,
TRAFFIC_LIGHT_YELLOW,
TRAFFIC_LIGHT_GREEN,
} traffic_light_t;
在這個例子中,我們宣告了列舉型別 traffic_light_t
,該型別有三種值。
來自 C 語言的衍生型別
衍生型別會基於某個基底型別,像是字元陣列的基底型別是 char
,但型別是陣列而非單一的 char
。以下是 C 語言的衍生型別:
- 陣列 (Array)
- 結構 (Structure)
- 聯合 (Union)
- 指標 (Pointer)
陣列 (Array)
陣列是線性 (linear)、同質 (homogeneous)、連續的 (continous) 容器或資料結構,主要的優點在於快速的隨機存取,以索引存取陣列元素的效率是 O(1)
。C 陣列本身在建立後即無法改變大小,動態陣列是以新增陣列後搬移元素來達成動態的特性。
Objective-C 另外提供 NSArray
(固定大小) 和 NSMutableArray
(可變大小) 兩種陣列型別。除非寫純 C 程式,應優先使用這兩種陣列類別。
結構 (Structure)
結構是用來表達由多項資料所組合而成的複合型別。一個常見的例子是二維空間的點:
struct point_t {
float x;
float y;
};
這個宣告定義了結構型別 point_t
,該型別包含了兩個屬性 x
和 y
。
在純 C 程式中用結構實作資料結構的 ADT (abstract data type) 或模擬物件導向程式的物件 (object) 是常見的技巧,但在 Objective-C 中可以直接使用類別來實作,這類技巧就變得不實用也不建議使用。
聯合 (Union)
聯合用來表達同一欄位中可交替使用不同型別的情境。以下是實例:
union data_t {
int i;
float f;
};
這個宣告定義了聯合型別 data_t
,data_t
物件可在 i
或 f
中擇一使用。
指標 (Pointer)
指標的用途是在程式中管理記憶體。Objective-C 沿用 C 語言的指標,故先前 C 指標的概念大抵上可通用。以下是一個實例:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
在這個例子中,pool
物件的型別是指向 NSAutoreleasePool
的指標。我們會在後文介紹指標和記憶體管理。
void
型別
void
是一個特別的型別,在程式中無法直接將變數指定為 void
型別,只能用在少數的情境。
在函式參數列 (parameter list) 中使用 void
代表該函數沒有參數;相對來說,在函式回傳值中使用 void
代表該函數沒有回傳值。如以下實例:
void f(void)
{
// Implement this function here.
}
函式 f
既沒有參數也沒有回傳值,這樣的函式不太實用。實際上函式不會這樣設計,這只是展示 void
的用法。
Objective-C 的萬用型別 id
Objective-C 引入一個萬用型別 id
,這個型別表示任何 Objective-C 物件,類似於 C 語言的 void *
(指向 void 的指標)。
雖然 Objective-C 衍生自 C 語言,卻有著 Smalltalk 般動態型別的特性。這牽涉到 Objective-C 的物件的行為,和一般熟知的 C++ 或 Java 的物件的行為有一些差異,這些差異無法單純從語法上看出來。我們會於後文介紹 Objective-C 的物件導向程式時介紹相關內容。
來自 Cocoa 或 GNUstep 的類別 (Class)
Objective-C 除了有 Smalltalk 風格的物件系統外,另外有一整套的類別庫,這套物件庫就是 Cocoa,而 GNUstep 是 Cocoa 的自由軟體重製版。Cocoa 分為三大類:
- Foundation Kit:和圖形介面無關的類別庫
- Application Kit:和圖形介面相關的類別庫
- Core Data:和物件持久性 (object persistence) 相關的類別庫
目前 GNUstep 沒有 Core Data 部分,只有前兩大類類別庫,而且在 API 上稍微落後於 Cocoa。
GCC 或 Clang 有 Objective-C 的編譯器,但沒有 Cocoa (或 GNUstep) 物件庫的話,連最基本的 NSObject
類別都無法使用。一個空有語法但沒有函式庫或物件庫的語言,基本上不太實用;所以我們會在先前的文章提到安裝 Cocoa (或 GNUstep) 的部分。
本系列文章的重心在 Objective-C 的語法上,不會觸及太多 Cocoa (或 GNUstep) 相關的內容。不過,Cocoa (或 GNUstep) 中有一些基礎容器或資料結構,可做為 Objective-C 程式基礎的模塊,我們會適時介紹這些內容。