前一陣子遇到一個需求:我們現在有一個 array,裡頭是一堆中文字串,現在我們想要讓這些中文字串按照一些個定的中文索引方式排序,像是使用筆劃、拼音…等等。搞了半天,發現只要在 compare 的時候,選擇特定的 NSLocale 就好了,但是蘋果自己的文件中,居然對於有這些 Locale 可以設沒什麼著墨,顯然很多時候光看蘋果的文件是不夠的。
我們先來看段使用筆畫排序的程式碼:
NSArray *a = @[ @"落魄江湖載酒行", @"楚腰纖細掌中輕", @"十年一覺揚州夢", @"贏得青樓薄倖名"]; NSLocale *strokeSortingLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh@collation=stroke"]; a = [a sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2 options:0 range:NSMakeRange(0, [obj1 length]) locale:strokeSortingLocale]; }]; for (NSString *s in a) { NSLog(@"%@", s); }
在 Mac OS X/iOS 中,所有與各種不同國家、語言中使用的不同曆法、日期格式、數字格式相關的資訊,也就是 NSDateFormatter、NSNumberFormatter、NSLocale 這些 class 底下,其實呼叫的是 IBM 的 OpenSource 專案 ICU,所以呢,ICU 支援哪些 Locale,NSLocale 裡頭就可以使用哪些 Locale。
在 ICU 的 Locale 中,我們需要注意的,就是可以在語系(像是 zh)與地區(像是 TW)之後,還可以繼續加上 keyword,keyword 可以是曆法、排序方式、貨幣與數字格式,於是指定以筆畫順序排序的中文語系,就會寫成 zh@collation=stroke。ICU 還支援拼音(pinyin)、Big5 編碼順序(big5han)、GB2312 編碼順序(gb2312)以及部首筆畫順序(unihan)。
由於系統裡頭已經有了一份 ICU library,我們也可以直接呼叫 ICU 的 C 或是 C++ API,做我們在上面做的這件事情。但是在 Mac OS X/iOS 上面想要使用系統內建的 ICU library 還頂麻煩,雖然我們可以讓 linker 直接去連結 libicucore.dylib,但是系統裡頭卻沒有放 ICU 的 header,所以還是要去下載一份 ICU 把 header 挖出來用,另外還要設一些 compiler 參數(詳情可以參考 Lukhnos 去年的這篇 Using OS X’s Built-in ICU Library in Your Own Project),或,我還有一小段程式,大概示範了怎樣使用 ICU 的 C 與 C++ API 排序。但,既然這麼麻煩,我們還是直接呼叫 Cocoa Framework,寫 Objective C 就好了。
說到 ICU 就要講一下前幾年遇到的一個慘痛經驗。前幾年參與一個專案,看到蘋果當時在 Objective C 上面時做了 Garbage Collection,就高高興興拿來寫了個 Mac App,結果呢,蘋果做出來的 GC 真是一踏糊塗,在打開了 ObjC GC 的狀況下,如果不是在 main thread 呼叫架構在 ICU 上的 Foundation Class,像是前面提到的 NSLocale、NSDateFormatter,記憶體就開始狂洩不止,也難怪蘋果最後放棄了在 run time 做 GC 這條路,選擇了 ARC,在 compile time 幫你加上 retain、release,但 ARC 也有一堆亂七八糟的疑難雜症就是了。
居然對於有這些 Locale 可以設?沒什麼著墨
這告訴我們一件事情:很多事情其實光看蘋果文件是不夠的。