最近這個月,從制作 CocoaPods 私有庫,到搭建 Git 服務器,大多數時間都花在了調研和試錯上。不過調研試錯這個事總是能讓你感覺到自己懂的只是皮毛這個事實(所以當你覺得天下無敵的時候那就去調研,TA 會讓你爆棚的自信心損失殆盡 ????)。以下文字就先記錄下關于團隊統一代碼風格這件事。
正文
對于團隊統一代碼風格的好處想必大家都應該清楚,以下我就羅列幾點相對比較重要的:
- 促進團隊合作,提高代碼的可讀性;
- 有助于 Code Review ;
- 甚至可以降低 Bug 出現的概率;
- 自行 YY .....;
代碼規范想必有部分同學會把它和個人習慣混為一談,其實代碼規范和個人習慣壓根不是一個層面上的東西。代碼規范針對的是團隊,而個人習慣僅僅針對你自己。制定代碼規范的目的是為了提高團隊協作的效率以及代碼的可維護性。因此,個人習慣和代碼規范擦出火花的時候,很明顯你應該遵守規范而不是你自己所謂的那些習慣。當然規范并不是一塵不變的,當產生火花的時候完全可以提議修改代碼規范,但是在規范修改之前,你必須要遵守舊的規范。因為團隊利益大于一切。
舉個栗子,
A 習慣寫 single line 的條件語句。
if (a) return;
而 B 自己的習慣是這樣,并且 B 還會使用一些格式化代碼(Xcode 的 clang-format,js 的 jsFormat 等)的插件來提升開發效率。
if (a) {
return ;
}
那么當 B 去修改 A 的代碼時,看著不順眼,就會把 A 的所有 single line 都消滅掉,因為 B 有插件,秒秒鐘解決戰斗。這就會導致 A 和 B 沖突的概率大大增加,甚至會造成很多不必要代碼合并的成本。想必大家看完這個栗子應該很清楚一個團隊制定代碼規范的重要性了。
一個團隊的代碼規范首先需要制定一份文檔,當出現任何風格沖突的時候,以文檔為準。因此團隊在開始之初就制定了一份規范。
制定規范文檔這是第一步,在這個基礎上能不能再提高些效率呢?
大多數 iOS 開發者應該都知道 Xcode 的插件 Clang Format。它是基于 clang-format 命令行工具的一個 Xcode 插件。
clang-format是基于clang的一個命令行工具。這個工具能夠自動化格式C/C++/Obj-C代碼,支持多種代碼風格:Google, Chromium, LLVM, Mozilla, WebKit,也支持自定義風格(通過編寫.clang-format文件)。
我們可以通過自定義 .clang-format 文件來實現自定義代碼風格。這是目前使用的一份配置文件。具體每項參數可以查閱這份文檔。
通過這個插件已經可以實現通過快捷鍵或者在文件保存時格式化代碼了,其實做到這一步已經節省了很多開發成本,但是這種方案也是存在一定缺陷。如果通過快捷鍵去格式化,容易遺漏;保存的時候去格式化,對于像我這樣時不時會按 Command + S 的人來說,Xcode 會變得稍顯卡頓。那么有沒有現成的方案可以進一步優化體驗?既然之前的方案有問題,就有必要繼續尋找更優解。
一輪 Google 之后,spacecommander 貌似符合大部分需求。它利用 Git Hooks 可以在 commit 之前驗證代碼風格符合規范,只有符合規范的代碼才允許提交,同時也提供 Shell 腳本來格式化一個文件,或者一整個 Git 倉庫。我猜想通過 spacecommander ,可以在不改變自己編碼風格(當然只限于格式,具體的命名規范,注釋規范還得參照具體規范文檔)的前題下,可以實現代碼風格統一。因為你開發過程中可以按照個人習慣來,commit 之前使用 spacecommander 提供的腳本對文件進行格式化。這樣只需要所有開發人員統一 spacecommander 的 .clangformat 配置文件就可以了。看到這里是不是發現這個輪子是不是剛好命中痛點。那么接下來我就簡單描述下使用步驟:
- fork spacecommander;
- 修改其中的 .clangformat 文件以滿足自己團隊的編碼風格,當然有能力也可以修改其中的 Shell 腳本,自定義一些功能;
- clone 到本地一個較為安全的目錄(別一不小心刪掉了...);
- 為了之后方便使用,可以把幾個腳本對應設置一個 alias;
- cd 到項目根目錄,執行 clangformatinit ,進行初始化(添加了一個指向本地 spacecommander 倉庫的 .clangformat 替身以及在 .git/hooks 中的 pre-commit, hook 相關的可以參見 Pro Git)
- 在提交代碼之前,spacecommander 都會通過 pre-commit 這個 Hook 來校驗修改過的文件,校驗通過才允許提交。
如何設置 alias 簡化命令
// 使用zsh 則修改 ~/.zshrc;bash 則修改~/.bash_profile
// 初始化
alias clangformatinit="/Users/SpaceCommander_iOS/setup-repo.sh"
// 格式化對應文件
alias clangformat="/Users/SpaceCommander_iOS/format-objc-file.sh"
// 格式化整個倉庫
alias clangformatall="/Users/SpaceCommander_iOS/format-objc-files-in-repo.sh
如果需要 spacecommander 忽略某個目錄下的文件的格式,則可以通過修改 spacecommander/lib/common-lib.sh 腳本來實現。默認它已經忽略了 Pods 和 Carthage 目錄。
// common-lib.sh 簡化版
function objc_files_to_format() {
optional_base_sha="$1"
directories_to_check
files=$(git diff --cached --name-only $optional_base_sha --diff-filter=ACM -- $locations_to_diff | grep -e '\.m$' -e '\.mm$' -e '\.h$' -e '\.hh$')
directories_to_ignore
echo "$files" | grep -v 'Pods/' | grep -v 'Carthage/' >&1
}
function all_valid_objc_files_in_repo() {
directories_to_check
files=$(git ls-tree --name-only --full-tree -r HEAD -- $locations_to_diff | grep -e '\.m$' -e '\.mm$' -e '\.h$' -e '\.hh$')
directories_to_ignore
echo "$files" | grep -v 'Pods/' | grep -v 'Carthage/' >&1
}
就此初步解決了 iOS 開發的代碼風格問題 ———— 一份代碼規范以及自定義的 spacecommander。
小結
這個問題看似有點微不足道,但的確花了毛一天的時間,才找到了較為合適的解決方案。但是在我看來是很值的。不僅能減少后續開發維護過程中由于代碼風格引起的麻煩(感謝 spacecommander 這個輪子),而且在調研的過程中,也接觸到了一些新的知識,比如 Shell 腳本語言。所以我很喜歡去干這種事情 ????。 接下來有時間會整理一篇 CocoaPods 私有庫相關的博客 ????。
有其它的解決方案,歡迎分享 ????