星期三, 四月 28, 2010

awakeFromNib 與 initWithNibName:bundle:

程式大了一些之後,我們常常會需要切換不同的 view 元件,一般來說,會把幾個主要的 view 以及它的 controller 包到獨立的 nib 檔中。一來方便管理,不會在 MainMenu.nib 中看到一堆 view、controller,甚至其他的 formatter 之類的。二來,可以依需要動態載入、釋放這些 object,降低佔用的系統資源。

而一般 view nib 檔中除了 view 之外,會把 File's Owner 指定為某個 controller 的類別,而這個類別通常是繼承自 NSViewController。

然後我們就可以在程式中使用
MyViewController * controller = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];
來載入這個儲存在 nib 檔中的 view。
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
是 NSViewController 提供的一個 method,用來載入存在 nib 檔中的物件,然後我們就可以對 controller 下操作的命令,一切看起來都很美好。

於是我們開心的在 MyViewController.m 裡面實作 awakeFromNib,在裡面初始化一些 UI 元件,一切依然很美好,然後我們利用
[placeHolder addSubview:[controller view]];
來把 MyView 顯示在某個 view 裡面,運作正常,美好的不得了。

不過問題來了,如果我們要在把 view 顯示出來之前對 controller 先做些事,就會發現:awakeFromNib 還沒有被呼叫過!所以裡面的初始化都還沒做!

整個流程的問題出在,awakeFromNib 會在所有存在 nib 檔中的物件被解開之後,發送給 "所有這些被解開的物件",而 "這些物件" 並不包括我們的 controller,因為它是我們在別的地方 alloc 出來的!也就是說,MyViewController 並不會在 [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil]; 之後接收到 awakeFromNib。因此,初始化的適當地點是,自己實作 MyViewController 的 initWithNibName:bundle: method,並在裡面做初始化的動作。

標籤: , ,

星期日, 五月 24, 2009

用 Objective-C 從二進位檔案(binary file)中讀入數值的 byte order 問題

要從二進位檔案中讀入數值,其中一個會遇到的問題就是 Little-endian 和 Big-endian,這和 CPU 的種類有關。Intel 系列的 x86 CPU 使用的是 Little-endian,而 PowerPC、ARM 等的 CPU 則是採用 Big-endian。endian 不同的問題,在以前 PPC 的時代算是相當重要,後來改用 Intel CPU 之後重要性就下降了許多,不過現在因為 iPhone 採用的 ARM 又是 Big-endian,所以這問題的重要性又大幅提升了,尤其許多新手設計師,之前可能根本沒碰過非 Intel 的平台,根本沒考慮過這問題,於是寫出來的程式就會讀到一堆詭異的數字。

首先我們要先界定 endian 的問題會影響到哪些資料型別。所謂 endian 不同,是因為高低位元在記憶體中表示的方式不同而造成的,Little-endian 將高位元放在記憶體位址較大的地方,Big-endian 則反之。舉例來說,0x00112233 這個 16 進位的數字,在 Little-endian 的機器上,在記憶體中呈現的是 33 22 11 00,而在 Big-endian 的機器上則呈現 00 11 22 33。因此,假如把在 Little-endian 機器上寫入的二進位檔拿給 Big-endian 的機器讀取,讀出來的數字就會出錯。

從這邊我們可以發現,要發生 endian 的問題,首先資料型別必須用超過 2 個 byte 來儲存才有可能,因此只用一個 byte 儲存的型別,例如 char,就沒有 endian 的問題。

至於 int、long 之類的整數型別,可以用 CFSwap[type][Big/Littile/Host]To[Big/Littile/Host] 系列函式來轉換。其中的 type 可能是 Int16、Int32 等等。使用方法如下:
int littleEndian = 1234;
int hostEndian = CFSwapInt16LittleToHost(littleEndian);
那麼 hostEndian 的變數值就是採用目前機器上的 endian 順序的 int。

不過浮點數就沒這麼輕鬆了。我們在前面的例子中還是用 int 來存放 littleEndian,因為就算 byte order 不對了,但它仍然是個 valid 的 integer,不過浮點數的每個 bit 有它不同的意義,因此一旦 byte order 解譯錯誤,它就可能變成 invalid float,導致程式直接當掉。

為了解決這個問題,Objective-C 中用了一個與 byte order 無關的資料結構來儲存這個資料,定義如下:
struct CFSwappedFloat32 {
uint32_t v;
};
typedef struct CFSwappedFloat32 CFSwappedFloat32;
可以看到,這個結構中就只有一個 data,uint32_t,Objective-C 就是用它來儲存這個 float 的資料(當然,此時系統並不會把它當成一個 float)。接下來的處理方式就是,將這個 uint32_t 裡面的 byte 頭尾互換位置,然後將這 32 個 bit(4 個 byte)轉化成 float。而這就是 CFConvertFloat32SwappedToHost 這個函式在作的事。整個的用法可以用以下的例子表示:
CFSwappedFloat32 littleFloat;
long buffer;
float hostFloat;

fread(&buffer, sizeof(long), 1, fp);
littleFloat.v = buffer;
hostFloat = CFConvertFloat32SwappedToHost(littleFloat);
當然,其中的 fread 可以用 memcpy、NSData 的 byte method 等等取代,只要把握住一個原則:「 CFSwappedFloat32 的內部是用 uint32_t 來儲存這個待轉換的 float」即可。

標籤: , , ,

NSNumber 與 NSInteger

在 Mac OSX 10.5 中新增加了 NSInteger 這個東西,但是這下就會跟 NSNumber 搞混,到底要用哪個?

基本上,NSInteger(以及類似的 NSUInteger)是為了解決在 32-bit/64-bit CPU 架構下,int 長度不同的問題,它的定義如下:

#if __LP64__ || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
也就是說,NSInteger 只是用 typedef 包裝過的 int/long,它是個基本資料型別(data type)。

NSNumber 則是個不折不扣的 Objective-C 類別,因此這兩者的使用時機就很明顯了,當你要把 integer 放到 Obj-C 的集合,例如 NSArray/NSSet 時,請用 NSNumber,不然其他的時候用 NSInteger 就好了。

標籤: , , ,

星期三, 十一月 07, 2007

Xcode 2.5 & 3.0 發表

Apple 前幾天發表了 Xcode 2.5 及 3.0,其中 Xcode 2.5 可以安裝在 10.4 Tiger 和 10.5 Leopard 上,而 Xcode 3.0 則只能安裝在 10.5 Leopard。

根據 Release Note,Xcode 2.5 除了修改了 2.4.1 的問題之外,最主要的功能就是在 Leopard 上為那些還沒有準備好要轉移到 Xcode 3 的專案提供一個比較平緩的轉移方式。

Xcode 3.0 則是一個大改版,包含了 Objective-C 2.0 的支援、Dashcode、Interface Builder 3 等等。

不過要注意的是,Objective-C 2.0 只能在 Leopard 上執行,因此必須搭配 10.5 SDK。假如要在 Leopard 上寫能在 Tiger 執行的程式,就必須嚴守 Objective-C 1.0 和 10.4 SDK 才行。

Xcode 可以在 Apple 的開發者網站下載。
Objective-C 2.0 的官方文件在 The Objective-C 2.0 Programming Language

標籤: , ,