Simply Patrick

Hopes can always go up, tears can only come down.

Reference Counting in WebKit

WebKit 裡物件的生命週期很多都是由引用計數 (reference count) 控制的,今天有空看了一下,發現幾個巧妙之處:

1) RefCounted 是個 template class,並且繼承自 RefCountedBase:

繼承 RefCountedBase 的技巧 (template hoisting) 是為了避免 template 實例化產生的 code bloating;而 destructor 裡的 delete static_cast<T*>(this); 則是為了避免不必要的 virtual table 產生,觀察 ~RefCountBase() 是個 non-virtual function 可以印證:

2) RefCountedBase 的設計相當輕量化, release 版本裡實際只會佔用一個 integer 的空間,而 debug 版裡則多了幾個輔助除錯的欄位:

m_deletionHasBegun 是用來確保當物件當參考計數已經小於 1 之後是不能再做 ref/deref 的動作的;m_adoptionIsRequired 是用來確保 adoptRef() 一定要被呼叫到,也就是 new 出來的物件一定要先用 PassRefPtr 來管理;而 m_verifier 則是用來確認物件不會同時被兩個以上的 thread 引用。

3) Reference counting 一般的做法都是把計數初始值設定成 0,而 WebKit 為了避免每次創建都需要做的 0 -> 1 以及銷毀時要做的 1 -> 0 的記憶體存取,則是把計數值初始值設定成 1 的,然後強迫要用 PassRefPtr 來管理新產生的物件。

4) 為了效能,引用計數的增加及減少是預設不保證 thread-safe。

所以 RefCounted 的機制要能運作正常,正確地使用 RefPtr 及 PassRefPtr 是很重要的,也就是如果你需要對 WebKit 做一定程度的修改的話,“RefPtr and PassRefPtr Basics” 這篇文章是必備的知識。

Continuous Integration and Android

在 Jenkins 上設定 Android app 的持續整合 並不困難,而且是頗讓人愉快的一件事,因為這充分體現了工程師懶惰的美德,要我自己去弄這些可以自動化的工作會要我的命,而剩下唯一的問題就是需要架設 server,但在雲端運算盛行的今日,你可以大膽地賭有人已經提供這樣的服務了。

CloudBees 提供的正是 “Jenkins as a Service” 這種服務,講白了就是可以把 Jenkins server 架在 CloudBees 的 server 上,你要做的就是把 sourec code 推到它提供的的 git repository 或是 GitHub 上即可。

如果你是獨立的 Android app 開發者,CloudBees 提供的免費服務其實還不錯,非常值得考慮使用:

附帶一提,Hudson/Jenkins 的創造人 Kohsuke Kawaguchi 目前就是在 CloudBees 擔任 Architect。

參考:

Cheating on iOS Games

其實我以前還蠻喜歡嘗試去破解一些軟體或是修改遊戲來作弊,純粹是想挑戰一下從中獲得一些樂趣。

最近的一次嘗試是修改 iOS 上的 Final Fantasy II,這遊戲應該是我剛上國中的時候玩的吧,忘了因為什麼原因沒有把它破關,一直是心裡的一個遺憾,畢竟我是從 Final Fantasy I 就開始玩的老玩家了。所以如今 Square Enix 在 iOS 上把它重製,但我已經沒有那個閒功夫再重頭從等級 1 慢慢練了,所以最快的方法就是破解作弊啦。

要在 iOS 上進行遊戲的作弊破解有幾個前提:

  1. 首先你需要是一台已經 JB 過的 iPhone 或是 iPad,因為需要在 device 用 Cydia 上安裝 gdb 來追蹤遊戲的運行。
  2. 再來你需要有 ARM Assembly 的知識,因為要能看懂程序的邏輯然後做修改。
  3. 你需要會用反組譯的分析工作例如 IDA Pro
  4. 你必須會基本的 gdb 操作。

我用的方法大致跟 用iPhone修改及调试游戏作弊版教程 是一樣的:

  1. 在 device 上安裝 openssh 及 openssl
  2. 用 usbmuxd 裡的 python-client 透過 USB 跟 device 做 SSH 連線
  3. 我是用 iFile 把 FinalFantasy2.app 從手機抓出來用 IDA Pro 做靜態分析。
  4. 再來就是比較苦工的部份,要找出那一段是遊戲裡增加經驗值或是 HP 的地方,通常可以從 function 的名字來判斷,例如這個有 LV_UP 的 function 看起來就很像是處理等級提昇的地方:

反組譯後的程序

可能的地方

找到可能的地方了,連上 device 來做實驗看看,我通常是直接設定中斷點,然後把 register 的值改掉:

(gdb) break *0x2789e
(gdb) commands
>set $r3 = 100
>c
>end

其他的一些能力值也可以如法泡製,這樣就可以輕鬆玩啦:

作弊

ARM Architecture for Mobile Developers

目前幾乎市場上所有的 mobile device 用的 processor 都是基於 ARM Architecture 的,而做為一位 Android 或是 iOS 平台的應用程序開發人員對於 ARM 有一定的認識雖非必要,但多了解一點對優化程序及偵錯問題是有很大幫助的。關於 ARM 的基本介紹,可以參考 Pierre Lebeaupin 的 A few things iOS developers ought to know about the ARM architecture

最基本要知道的是 ARM Architecture 是有分版本的:

ARM Revision History

目前主流的 Smartphone 都是採用 ARMv7a 的架構。

而另一個需要了解的觀念是 ABI (Application Binary Interface),要了解 ARM 的 ABI 最快的就是參考 Application Binary Interface for the ARM Architecture 裡的這張圖:

ABI 地圖

從圖上可以看到在 ABI for the ARM Architecure 實際涵蓋了許多範圍,各個平台的 ABI 通常就是參考標準的 ARM ABI 然後把不明確的地方或是有差異的部份定義清楚。

Android 開發人員可以參考 docs/CPU-ARCH-ABIS.html,使用 Android NDK 開發的話,目前就有分 armeabi 及 armeabi-v7a。iOS 的話除了第一代 iPhone 外目前都是 ARMv7 架構,但目前似乎只找的到這份 iOS ABI Function Call Guide 可以一窺 iOS上的一些實作細節。

Build iOS App With Rake

上次說到 rake 是幫忙 iOS App 本地化的好用工具,但其實幾乎所有要自動化的事情都能請它幫忙, 例如寫 game 的人需要做的 sprite sheet 也能請 rake 自動產生

除此之外,準備各種不同的 build 也是頗煩人的事,如果你也用 rake 的話就可以用 wox 這個 gem 來幫忙,下面的範例是 wox 文檔建議的方式:

使用 wox 的優點是不同 task 的 configuration 是可以繼承的,例如 ipa:app_store 套用的就是上一層 build:release 的設定,但用的是上傳 App Store 用的 provisioning profile。同樣道理 ipa:adhoc 用的是 Team Provisioning Profile。而如果你有用 TestFlight 的話,就可以繼承 ipa:adhoc 把做出來 的 ipa 檔直接上傳到 TestFlight。

關於 TestFlight,可以參考 Polydice 的介紹

iOS App Localization Using Rake

一般要幫 iOS App 本地化,其中比較繁瑣的就是翻譯 xib 檔案中的字串,通常是需要下面三個步驟 (可參考 ICanLocalize 的教學):

  • 使用 ibtool 把字串從 xib 中抓出來
  • 找人翻譯字串
  • 使用 ibtool 再把翻譯好的字串放到 xib 中

不過我是不能忍受這些工作要手動進行的,所以最好的方式是用 rake 來自動化這些事情:

首先請 rake 幫忙把字串檔都產生出來:

$ rake l10n:genstrings

翻譯完這些字串檔後,再請 rake 幫你放進去:

$ rake l10n:usestings

不管你有幾個語言或是幾個 xib 檔都是一樣的步驟。

LiveReload and Octopress

雖然平常沒什麼時間寫 blog,但總是希望寫的時候能夠專注在所寫的內容,而不必被發佈 blog 的一些瑣事而干擾,例如現在雖然已經習慣用 Pow 了,然後把 rake watch 開著,但要預覽時還是要回 Chrome 按一下 Reload 才能預覽目前的內容,有點小煩,所以看到有 LiveReload 這種好東西當然要拿來用啊。

簡單描述一下安裝的步驟:

  • 安裝 guard-livereload:
1
$ gem install guard-livereload
  • 修改 Gemfile 把下列這一行加進去 ‘guard-livereload’:
1
gem 'guard-livereload'
  • 再來是改完 Gemfile 後的標準步驟:
1
$ bundle install
  • 產生一份基本的 Guardfile:
1
$ guard init livereload
  • 再來要修改 Rakefile 把啟動 guard 的動作放到 rake watch 裡面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
desc "Watch the site and regenerate when it changes"
task :watch do
  raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
  puts "Starting to watch source with Jekyll and Compass."
  system "compass compile --css-dir #{source_dir}/stylesheets" unless File.exist?("#{source_dir}/stylesheets/screen.css")
  jekyllPid = Process.spawn("jekyll --auto")
  compassPid = Process.spawn("compass watch")
  guardPid = Process.spawn("guard")

  trap("INT") {
    [jekyllPid, compassPid, guardPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH }
    exit 0
  }

  [jekyllPid, compassPid, guardPid].each { |pid| Process.wait(pid) }
end
  • 然後執行:
1
$ rake watch

應該可以看到類似下面的訊息回應:

Starting to watch source with Jekyll and Compass.
Please install growl_notify or growl gem for Mac OS X notification support and add it to your Gemfile
Guard is now watching at '/Users/patrick/src/simplypatrick.heroku.com'
LiveReload 1.6 is waiting for a browser to connect.
Configuration from /Users/patrick/src/simplypatrick.heroku.com/_config.yml
Auto-regenerating enabled: source -> public
[2011-12-03 22:53:07] regeneration: 118 files changed
>>> Compass is watching for changes. Press Ctrl-C to Stop.
  • 安裝 LiveReload extension (Chrome 請按這)
  • 最後打開會修改的網頁,如果是使用 Chrome 要記得把網址列旁的 LR 按鍵按下才會生效喔,我在這裡卡了有點小久。

在 27” 螢幕上,左半邊寫文章,右半邊瀏覽器即時預覽,這樣的感覺太好了!

Git Workflow

由於 git 要開分支很容易,但要怎麼運用分支來管理就是一門學問了。在網路上最常看到的是這個方法:

a-successful-git-branching-model

除了這個方式外,另外一種常用的方法是如 git help workflows 所描述的。為了容易理解,我用 Pencil 畫了這張示意圖:

workflow

雖然前後兩張圖用的 branch 的名稱及用途不大一樣,但有些基本道理是相同的:

  • 平常只從穩定的分支 merge 到不穩定的分支。這裡的穩定及不穩定是相對的,例如第二張圖中 maint 比 master 穩定,而 master 又比 next 穩定;在第一張圖中,release 比 develop 穩定,hotfix 又比 develop 穩定。我說的 merge 是 git 中 branch to branch 的 merge,例如 merge A to B 是讓 branch A 所有的改動都合併到 branch B 中。
  • 在適當的時候可以從不穩定分支合併改動到穩定分支,例如第一張圖中的 developer -> release -> master 的合併,又例如第二張圖中的 next -> master -> maint。
  • Bugfix 應該 check-in 到最舊且需要這個改動的分支,然後再根據上一條規則的作法傳佈到較新的分支上。這樣做可以確保不會有忘記 merge 的 bugfix。
  • 新 feature 都應該在獨立的分支中開發。

不管是用那一種方式,善用分支來管理產品的開發及產品發佈已經是個軟體開發人員必備的技能了,如果你還不熟悉,趕快找個機會把這些方法用到你目前正在做的工作上吧。

AppCode: Make You a Happier iOS Developer

“XCode doesn’t look like a software tool that’s developed by Apple at all. It is clear Steve Jobs was not involved creating this tool.”

我完全同意這句話,從 XCode 3.2 用到現在 4.2 版還是覺得不順手。寫程式最怕的就是被其他事干擾而分心,IDE 如果有什麼地方讓我工作效率不彰,我通常很快就會受不了然後分心去解決效率問題先,因為不能馬上解決會一直困擾我,而 XCode 就是個會讓我一直要傷腦筋的工具,但是為了要在相對精美的 iOS 及 iPhone 上開發程式,XCode 是唯一的選擇。

Tom van ZummerenAppCode: IntelliJ for Objective C! 裡把 AppCode 比 XCode 還好的地方都整理出來了。我自己試用 AppCode 後也覺得 JetBrains 是有備而來的,用起來跟 IntelliJ IDEA 一樣地流暢,讓寫 Objective C 也能是個愉快的過程。

AppCode 主要贏在程式編輯功能,但偵錯功能感覺弱了一點,例如:

  • 我找不到 NSLog 輸出的訊息會跑到那裡
  • Debug 時輸出的訊息沒有 XCode 完整
  • 不會自動停在 first-chance exception

所以實際開發時還是要 AppCode 及 XCode 同時開啟,買個大螢幕會比較好:

AppCode and XCode together

Git and SourceTree

由於 Git 在 Linux/Windows 上都沒有比較好用的 GUI client,我向來是習慣直接在命令列敲指令,好處是簡單而且快,比較大的缺點就是瀏覽歷史記錄時比較麻煩,陽春又醜的 gitk 我通常是不得已才會去開它,所以換到 Mac OS X 上後,簡單好用而且開源的 GitX 自然就成為我的最愛,但 GitX 的更新實在有點慢,愛嘗鮮的我便一直注意著是否有更好的選擇。

後來有試用過 Tower,整個 UI 比 GitX 漂亮許多,使用設計上也相當不錯,正想說可以’定居’下來,沒想到中間殺出個 SourceTree,而且因為這個產品被 Atlassian 收購了,在 Mac App Store 正免費大放送中,估計之後會有跟 BitBucket 更緊密的功能整合。

SourceTree App

本來 Mac OS X 就是讓 programmer 感覺很舒服的開發環境,有了 SourceTree 之後變更棒了。