The Old Blog Archive (Traditional Chinese), 2004-2009

Archive for the 'tekhnologia 技術或者藝術' Category

碧娜.鮑許 (Pina Bausch) 逝世, 1940-2009

烏帕塔舞蹈劇場的消息:

Heute morgen starb Pina Bausch, die Tänzerin und Choreographin des Wuppertaler Tanztheaters. Ein unerwarteter schneller Tod ergriff sie fünf Tage nach einer Krebsdiagnose. Noch am vorletzten Sonntag stand sie mit ihrer Company im Wuppertaler Opernhaus auf der Bühne.

(烏帕塔舞蹈劇場的舞者及編舞者 Pina Bausch 今天 [2009年6月30日] 過世了。五天前她才剛做完癌症檢查,就這樣意外地匆匆過世。上上星期天,她還與她的舞者們一同站在烏帕塔歌劇院的舞台上。)

意圖

看別人的 code 最困難的地方,大概莫過於了解他人的意圖:為什麼你要採取這個路數來走。有些時候 code 很像雕塑,往往留下來的成品,是在揚棄了不必要的部份,或是拋棄了許多路徑之後,才得到的結果。

所以意圖 (intention) 有兩個部分:「為什麼」採取現在的手段,以及「為什麼不」採取其他各種可能存在的手段。

程式教材的文字為什麼往往比 code 要長上數十、甚至數百倍的原因就在這裡。另一方面,這也就是為什麼抄襲跟模仿不容易達到跟原創一樣結果的原因:因為前兩者都只看到了既存的結果,而沒有辦法體現出原創所採取(以及所不採取)路徑所意欲解決的問題──而那些問題很可能因為原創的出現而消失,使得抄襲模仿者甚至無法察覺問題的存在。

看懂 iPhone view controller 記體體管理的原則(或者:didReceiveMemoryWarning 到底在搞什麼鬼?)

當 iPhone 或 iPod Touch 的記憶體不夠用的時候,當前 app 的所有 view controller 物件都會收到 didReceiveMemoryWarning 呼叫。你可以把它當成像 NSNotification 一樣的事件。

系統預設的 didReceiveMemoryWarning 實作(你一定用得到,因為你得呼叫 [super didReceiveMemoryWarning])是,如果當前 controller 所擁有的 view 沒有其他 superview (通常代表意思是沒被顯示出來,例如,埋藏在 navigation chain 或其他 tab 當中),那這個 view object 就會被是放掉。

如果你的 controller 只有這個 view 物件,事情就到此為止。不過我們都知道人生從來不是這麼簡單的。

尤其,如果你的 controller 當中宣告了從 nib 載入了 IBOutlet 物件。根據 Apple 文件,xib 載入的時候所有的 IBOutlet 物件都已經至少有了 retain count 1 。這些物件通常也都被你的 view 物件所擁有,所以通常等你用到他們的時候,他們的 retain count 都至少是 2 。

但是你的 view object 被放掉時,這些 IBOutlet 還被 retain 著。所以你要做兩件事:

  1. 在 view 被放掉時,也同樣放掉你的 IBOutlet
  2. 在你的 controller 被 dealloc 到時,也放掉你的 IBOutlet ,同時將他們的 pointer 設到 nil

iPhone SDK 一直到 2.2.1 都沒有好方法讓你做到 1. ,所以請參考 Craig Hockenberry 的這篇文章 ,簡言之就是要去 override -[UIViewController setView:] 才有辦法在 view 被放掉時也同時放掉那些 IBOutlet

(雖然事實上,如果你照著 Apple 最新的 coding 規範,將 IBOutlet 都設為 readwrite retain property,那麼在你的 controller 下次再度載入 view 時,這些舊 IBOutlet 都會被放掉,但是呢,如果你的 IBOutlet 很肥,例如滾輪式選單,那麼最好是放掉他們)

然後呢,在你的 controller 要被 dealloc 的時候,記得放掉你的 IBOutlet 物件,同時將指標設到 nil。Apple 文件有說明原因:因為當你最後呼叫 [super dealloc] 的時候,你的 view object 會被放掉,然後如果你已經做了 1. ,view 被放掉時去釋放 IBOutlet,偏偏你早就已經在 dealloc method 前半部 release 了他們,那麼接下來就會有人對死掉的指標值(所謂的「當夠了物件」──dangling object)送出 release 訊息,那就爆炸了…

總之,iPhone app 在記憶體使用上的最高指導原則是 lazy loading。要用的時候才載入,不用的時候就放掉且將指標值設為 nil ,最不容易出錯。這也是 Apple 從 Objective-C 2.0 時代開始大量推薦使用 @property 的另一個原因。

近期投影片彙整

以下是最近幾個月所做過三場演講的投影片彙整:

學 Python: 兼談成人的語言習得

代替一篇欠一位朋友很久的文章。

曾經有位朋友問我當初是怎麼把 [C, C++, Ruby] 等語言學起來的,但是這種事就跟問我當初是怎麼回會腳踏車或是學會騎機車一樣,為已經模糊的記憶加油添醋的成份太多了。

最近倒是因為有個新的工作項目,雖然屬於熟悉的領域,但是很多相關的工具都還沒準備好。這似乎是個學新語言的好機會。於是想說來學 Python。

幾年前,我快退伍的時候,當時跟一位 PHP 屬於神的等級的學弟請教,如果這時代要寫程式,應該學哪些東西。在許多當時聽來相當陌生的名詞當中,Python 是其中一項。但我後來卻去學了 Ruby ,而且一直沒有動機學 Python ,因為:

  1. Python 跟 Ruby 同質性太高。至少對我來說他們都想解決同樣的問題
  2. Ruby 的 OO 比較對我的胃口(Smalltalk 系、與 ObjC 有表親關係)
  3. Ruby on Rails

不過因為新的工作內容,需要找一個參與各方都會的語言來做為工具開發用,突然讓 Python 變成了一種必要(我是唯一在工作開始前還沒有真正接觸過 Python 的人)。

目前採用的方法、進度跟心得是這樣的。

我沒有找書來看。唯一憑藉的是 Python 官方網站上的文件。這其實還蠻危險的,因為官方網站的文件範例有點稀少(好比說在講解基本型別時,很多細微之處沒有舉很多例子)。與此相關的是 Python 3 與 Python 2 有很大的差別,問題是 Python 3 網站上的文件沒有適時標出問題所在。好比說,所有 Python 2 教材裡關於 map(lambda x : x * 2, (1, 2, 3)) 的例子到 Python 3 上面執行結果絕對不是教材說的那樣。

過去學其他程式語言,大體上最重要的幾個一定要知道的東西,目前也都過了一遍。一般說來一個程式語言最重要的資料型別是字串(這個真的在語言教學中被大大低估),尤其是 Unicode 的問題,任何在這個年代撰寫程式的人都有機會碰到。然後就是控制結構,知道迴圈跟條件分支怎麼寫。再來就是語言特性了,好比說 Python 就是 tuple/list 的特性、怎麼樣使用 map-reduce 等操作(後者在 Ruby 有個非常不像話的名字叫做 inject …)。不過我還沒碰觸到 Python 的 OO。

知道了控制結構跟基本型別的操作,接下來就是 stdio、檔案開啟、取得系統物件(例如像 sys.stderr, sys.argv 這類每種語言教法跟意義稍稍有點不一樣的東西),然後就是看 library reference 找自己需要的程式庫來完成手上的工作。粗淺的心得:SQLite 內建支援真不錯。Regex 似乎沒有 Ruby 方便(但是看來沒有什麼語言 regex 能比 Perl 方便……)

我跟朋友們說,其實成人學語言是相當尷尬的。一方面,你不太需要其他形式上的語言知識(你不再需要知道什麼是名詞、動詞;你不再需要知道什麼是浮點數、Unicode字串);另一方面,你卻也失去了從頭學語言的緩慢樂趣。尤其如果是為了完成工作的需要而學語言,慢慢嘗試探索、從笨拙的風格慢慢學習精簡、符合「成語」(idiom)的用法,這些都變成了一種時間的奢侈。

然後可以想見的是,這樣學語言,一定就會在某些幽微之處撞牆,而且死得很難看。君不見大家都說直接用 printf 寫 SQL statement 有多危險,但是你需要某種緩慢的閱讀速度,才會了解問題的根源跟解法。泰西諺語所謂:A little knowledge is a dangerous thing.

也就是說呢,我以前一直在尋找好比說「給 C++ 程式設計者的 ObjC 快速轉換入門」或是「給 C 語言程式設計者的 PHP 快速入門」這種可以只要拿著小抄一一對映過去就的指南,其實是不存在的。或者說,天底下沒有這麼便宜的事情。所有語言的背後或許全部都是共通的(這是很重要的假說),但是至少人間世的語言間(包括了程式語言)沒有存在完美的、唯一的一對一映射。

另一個困難在於,即使很快地克服了語言外貌所造成的障礙(熟悉了語言的文法),但是「習慣用法」(convention)才是最困難的,而那完完全全是時間的產物。Convention 除了包含「大家都習慣怎麼說/解決這個問題」外,還包括了「有那些不具文的、遇到了就要閃躲的坑洞」,更上一層甚至還包括了「直覺上就應該採取的路徑」或「直覺上就知道這是不可行的路徑」這種東西。

當然,最終來說,成人語言學習還是要回到任何語言學教科書開頭就會教你(而很多論辯語言的人都從沒搞懂)的兩大基本不同概念:能力(competence)與表現(performance)。你只要能用一種語言來從事活動(例如:閱讀別人的 code、靠著 copy-and-paste 解決問題),都可以說是具有一種語言的能力。但是你說得怎麼樣、寫得怎麼樣,上不上得了檯面,則是有可以評量的標準。其實,自知之明(know thyself!)講的也就是這麼回事:知道自己有能力並不代表能夠有好表現,你得惦惦自己有多少斤兩,有多少「才調」(tsâi-tiāu)。具體來說,會了英文不代表寫出來的東西不會被 native speaker 拿去投稿 engrish.com,「知道」什麼是 SQL 跟怎麼樣寫出不會被人 inject (此處 inject 不是 Ruby 那個 inject)的 SQL 操作也是天差地遠兩回事。

這當然跟自己的目標很有關係。如果你的目標不是「我要精通 x 語言」且沒有付出相對應代價、通過相對應考驗(這也仍然是兩回事,付出過苦工,跟上不上得了檯面沒有必然關係),但是卻又有著工作上的責任背負在身,此時最好還是遵循這個簡單的指導原則。這是一句鄉民用語,謂:「閃開,讓專業的來」。

至於我呢,在達成了內部工作需要的目標後,或許還是應該真的找一本好的書來讀,然後尋找其他用得上新學語言的場合來精進。如果有時間的話──因為忙碌而造成沒有辦法緩慢下來,以及因為有了既成的知識而再也無法像初心者一樣純真地看待任何幽微的細節,這兩者或許是成人語言習得 (language acquisition) 的萬惡之源。

寫程式真難

正所謂「啊嘶漫長,生命苦短」(ars longa, vita brevis)。

有一天我同事在 review 我多年前寫的 code ,他問我:為什麼明明可以用 Objective-C 做的事情,你卻偏偏要用 C++ 來做?

(明明 Cocoa 就有字串相關的操作,為什麼要把字串轉成 C++ 字串,操作完再轉回去 Objective-C 呢?因為那時我不知道嘛……)

後來我們又在另一個場合聊到這個問題。我說:熟悉一個 framework 比熟悉語言還難啊…

同事說,也是啦,熟悉 framework 之後還要熟悉 convention…

我說,熟悉 convention 還不夠,你還得熟悉原廠有哪些 bug …

因為最難的事情我們OS平台幫你搞定了…

… 所以困難的事情才正要開始。

這大概是在邁入多核 64-bit 又要考慮 multithreading 又要考慮跨平台的年代裡開發 framework 的某種感想吧。

看舊 code

我第一次幫人家撰寫命名跟縮排法則是在很早、很早之前的事了。訂立這些規則是一件很有樂趣的事。或許這是某種奇怪的愛好。後來在棄絕的那些漫長的日子中,曾經一度把某一版 MLA Style Guide 給整本 k 完,而且竟然對於「到底句點要放在括號前還是括號後」或是「在又有引號又有括號的句子裡,句點要放引號裡、引號外、還是括號外?」這種事情覺得津津有味。這一定是某種不正常的表現。

重新寫起程式之後,倒是花了相當、相當、相當長的時間,才又像某種緩慢的吸體一樣,把對命名和縮排規則的看重,變為自己的一部分。然後看著自己從前寫的 code ,就會不可思議起來:天啊,我真的當時允許自己寫出這樣的東西來?!

但即使到了現在,我們仍然不時會為命名規則所苦。這裡講的是廣義的命名規則,還包括了標點符號、斷字、大小寫。我們會為了,好比說,Objective-C 的 instance variable 到底要用底線開頭(Apple/WebKit 風格)還是 m_ 開頭(業界普遍流傳、或者說某種被 MS 污染〔誤〕的 C++ 風格),就讓我們頭痛不已。那麼 Interface Builder 相關的成員呢?

命名麻煩的地方在於,它會隨著時間和潮流的改變而跟著變化。甚至,其實命名或許是第一個反應變化的事物,因為世界本來就是在命名之後才生的(這一點可以扯上更多思考與說法,在此不表)。當我們發現 Apple 竟然自己有 sample code 開始使用 mSomeVariable 的時候,著實訝異,卻還猜不透:這種命名改變背後究竟是被什麼考量所驅使的呢?

然後是看到大公司發表的 C++ 寫作風格。閱讀該文件,有如某種經文研究,裡面竟然每一條規則都以某種詰問或者至少是 pros and cons 體裁撰寫。

風格沒有一定,貴在一致和便利。但是大公司的寫作風格跟我認知或習慣所用的很不相同。有的甚至完全逆著我的考量而行之,有的則是在、例如說、前十條規則如此成立之後,就不得不然。

另一方面,這種由諸多規則堆積起來的風格,必有相當多的例外、互相衝突之處(於是又回到了以前學外語時,課堂的鐵律:規則是為了例外而存在的……),以及某種因為要想辦法推廣至每個環節,而某種制度必然帶來的不快(照說風格跟規則不至於帶來情緒……),但是這個世界往往以我們難以理解的方式彰顯自身。我們以為掌握風格,結果是風格在撰寫我們。

不過,再怎麼說,我應該再也不會、也不敢、也不願,寫出像 fooidx (fooIndex), barcnt (barCount), lahlahrfcnt (lahlah… WhatThe????) _ui_config_button (_configButton…. why_is_this_stuff_named_this_way?!!) 這樣的東西了吧。我也會記得變數跟 operator 之間一定要有空白、大括號要打在正確位置、還有絕對不斷行(這一點是從 Delicious Monster 作者 Wil Shipley 學來的,他曾經說過類似人類發明寬螢幕就是給你寫程式不斷行用的、類似這樣的話)。

就好像我會記得大標題裡面 a 不能大寫但 The 首字要大寫那樣……

iVanilla: 一個已經停止的 OpenVanilla 實驗

iVanilla 的想法是由 ycchang 於 2007 年十月的時候提出的。最早的想法是希望在 iPhone/iPod Touch 上利用當時的 “Toolchain” 製作出一個簡單的、作為概念實證 (proof of concept) 的 OpenVanilla 實作。所使用的方法也是當時各家 iPhone/iPod Touch 輸入法外掛所使用的 dynamic library insertion (透過 DYLD_INSERT_LIBRARIES 環境變數指定)。

對我們來說,iVanilla 的 code 主要還是在驗證 OpenVanilla 框架的可移植性。用 C++ 簡單撰寫的 “MinimalLoader” 實作了所有 OV 模組所需要的基本功能:對於組字區與選字列的操作。

這個實驗是在 2007 年 10 月至 11 月間進行的。ycchang 並於去年的 COSCUP 2007 中簡單討論過 iPhone/iPod Touch open source toolchain 的概況。我也在 ycchang 之後簡單介紹了符合 OpenVanilla 框架規格的 minimal loader 的實作方式。

不過,受限於我們各自的狀況,我們後來都無法繼續關注 Toolchain 的發展。另一方面我們傾向認為,使用非官方的工具開發這類型系統軟體,還是有不少潛在問題。(我個人並沒有在 iPod Touch 上輸入大量文字的需要,實驗的動機也因此偏向 OV 驗證更甚於實用)。「非官方」的 iPhone/iPod Touch 輸入法方案已經有相當多種,從許多消息來源觀之,Apple 官方也應該會推出自家的亞洲文字解決方案。

本次送進 OpenVanilla source repository 中的 iVanilla 是當時完成的 SimpleBPMFSimpleCJ,亦即簡單的傳統注音輸入法及倉頡輸入法。各自目錄中的 README.txt 有更詳細的說明。

由於我們都很久沒有在近期的 Toolchain 及 firmware 上編譯這個實驗,我們無法確定現在的 code 仍能使用。另外,我們也無法回答任何跟實驗有關的問題。本次釋出的 code 係以現狀提供 (“AS IS”)。請自負任何使用上的風險。

iVanilla 中所使用的 OpenVanilla 程式碼,以及 iVanilla 的 MinimalLoader、平台相依的本體部分,都以 OpenVanilla 的 New BSD License 授權方式釋出。如果你要將 iVanilla 使用在你的程式碼中,請遵守程式碼的授權。程式碼的著作權為作者群所有。

中和的永和路,Vista 64 的 System32,Cocoa 的 NSString

住台北縣的人可能聽過這一句

永和有永和路,中和也有永和路, 中和有中和路,永和也有中和路; 中和的中和路有接永和的中和路, 永和的永和路沒接中和的永和路; 永和的中和路有接永和的永和路, 中和的永和路沒接中和的中和路。

最近發現,世界上存在不少類似結構的東西,例如

Vista 64 有 System32 ,也有 SysWow64,System32 裡面裝的是 64-bit 的 binaries,SysWow64 裡面裝的是 32-bit 的 binaries。Vista 64 的 64-bit binaries 不能放進 Vista 64 的 SysWow64 裡,Vista 64 的 32-bit binaries 不能放進 Vista 64 的 System32 裡。

又或者

Cocoa 有 NSString,Carbon 有 CFString,Cocoa 的 NSString 可以接 Carbon 的 CFString,Carbon 的 CFString 可以接 Cocoa 的 NSString。Carbon 的 CFString 沒有 garbage collection,Cocoa 的 NSString 有也可以沒有 garbage collection。Carbon 的 CFString 可以接到 Cocoa 沒有 garbage collection 的 NSString 裡,Cocoa 有 garbage collection 的 NSString 不能直接接到 Carbon 的 CFString 裡。

對,總歸一句話就是,不鬼打牆時候就不會覺得這樣的設計會鬼打牆,鬼打牆的時候就真的覺得這樣的設計真是鬼打牆……

Next »