原文:How Apple could improve Their Developer Tools
作者:@Stanislav Pankevich
譯者:CocoaChina--Draven21
我和我的同事Alex Denisov之前就開發者對Xcode使用和iOS開發的體驗性問題進行了數次探討,這篇文章是對我們談論內容的總結。
對於我們研發出產品最終的性能和Xcode能否提高開發者的工作效率這些問題,由於我和Alex Denisov的觀點產生了明顯的分歧,所以我非常願意把我們的觀點分享出來和大家交流。
主要有以下幾個方面:
開發環境的問題
Xcode
project.pbxproj
圖形化 VS 語義(“鼠標驅動的開發”與“鍵盤驅動的開發”)
Auto Layout
Xibs/Storyboards
Focused tests
有諸多問題的集成工具為什麼至今還在?
衡量一個集成開發工具的標准很模糊,換言之可能根本沒有標准去衡量它:Monoliths are Bad Design… and You Know It。
我們都知道,集成開工具發是事物從出現,成長到走向成熟的必經之路。但某些觀點認為集成開發工具有必要分解成不同功能模塊的組件,這樣每個組件都可以遵循 單一職責 的原則演變並且對系統的其余部分沒有影響。按這種角度來說,LLVM就是從GCC集成編譯器中分離出來的,Carthage,“分散依賴理論的創始人”反對有更多的像CocoaPods這樣的“集成式”工具,模塊化的AFNetwork 2也是由集成式的AFNetwork 1中演化而來,這樣的例子還有很多很多。
這有個例子,是關於蘋果公司如何修復它眾多集成工具之一:Storyboards的。我們花了好幾年時間才從蘋果公司得到的“官方”的功能,將巨大的Stroyboard文件分割成一個一個更小的single-story-foused,像RBStoryboardLink這樣的開源庫則被引入的更早,在2012年: RBStoryboardLink被廢棄了 。
除了上面的Storyboards被優化的例子,仍然有很多集成工具,讓我們看看它們中最有爭議性的一些。
Xcode
顯然Xcode本身就是一個最大的集成工具:它的主要模塊包括文字編輯器和界面搭建器,其它一些是和編譯設置管理功能相關的,比如蘋果賬戶管理,源代碼控制集成等等 為什麼Xcode糟透了 。
我們不明白一個巨大的應用做這一切背後的原因,但我們能看到在Xcode7中純代碼文件和xib文件之間的轉換從時間上看可能要花10秒以上,也能看到模擬器的掛起,崩潰或者重啟(工作一天可能會看到5次以上的黑屏)。我們從不使用源代碼控制這個功能,因為它沒法和 SourceTree 這樣的專業的源代碼管理工具相比,甚至是這個工具僅僅是最普通的命令行。
目前,越來越多的人轉去使用AppCode--一款更加注重代碼編寫的集成開發環境。它並沒有圖形開發的恐懼和其他Xcode內嵌的模塊,但卻提供了更好的代碼分析和重構工具。我相信,使用AppCode的開發者有更高的效率,因為他們可以不用去關心那些不是真正需要的事情,比如 CocoaPods 的支持,我認為這不是一個像AppCode這樣“聰明的”IDE所應該有的,有時候看起來為了趕時髦,某些IDE總想讓自己具備所有的功能。
project.pbxproj
project.pbxproj是我認為的僅次於Xcode的第二大集成工具。
我打賭如果你曾經有機會把這個文件從頭讀到尾的話,你一定和我有同樣的印象:這個文件不適合人類管理,我會在心裡想它的設計就是反人類的。
模稜兩可的標識符在project.pbxproj文件中隨處可見 (867CFE661BFFDC5E001F85A8 是一個 /* ViewController.m */正如你所知道的),除了有很糟糕的格式外,還有很多東西是我們所無法控制的。想讓下面這些東西變得可閱讀和可編輯麼,純文本文件是人們很容易理解和維護的:
Project structure
Configurations
Targets
Schemes
Build Settings
Code signing details,
Run Scripts (yo shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";)
Build Phases (yo 74E916613A2758307FB74A44 /* Embed Pods Frameworks */ = {),
我們還沒有設法讓CMake來位置iOS的項目結構,但是我堅信下一個.pbxproj文件的代替者將會受一些成熟的編譯系統的啟發,像Make,CMake,Ninja,Gradle等,他們都基於文本文件,更重要的是它們的設計是符合人類的。
下面我們來討論一些組織我們捨棄古板的.pbxproj結構的問題。
分組vs文件夾
我們根本不需要分組,因為每個分組總是和對應一個真實的文件夾。甚至可以認為分組是反模式化的,如果一個人用分組來設計的項目結構和真實文件夾的結構不同的話,這通常表明他缺乏對項目真實文件結構的理解,而僅僅關心的是表面的邏輯結構。
對這點我早就提到過:我們已經准備開始用CMake來替換掉Xcode iOS的項目架構了,並且我們也准備開始用和LLVM的開發者使用的幾乎一樣的_CMakeLists.txt_的方式來維護和開發我們的app的項目架構。
我專門去問了我的安卓開發同事,他也確認在安卓開發中沒有組的概念,這樣當你向項目中添加一個新文件夾,Git的工作樹種除了添加那個文件外其他什麼也沒有,而這一點和Xcode剛好相反:當我們像Xcode中添加文件時,在project.pbxproj中也會記錄該文件的入口,這樣會導致我們在merge/rebase操作時產生沖突。
xcconfig文件:包裝和繼承
我們有比xcconfig更好的管理和設置第三方組件,很好地抽象化編譯環境的配置的工具嗎。
這個問題 是我們在2006年提出的,現在還在這:
自動換行:
我寫了一行很長的代碼,超出了我的屏幕:我怎樣才能將它截斷,C的方法不可行。
你可以在編輯器中打開wrapping設置,但是.xcconfig文件對自動換行支持的並不是很好。
自動換行的特性可以讓代碼更具可讀性,並且降低合並代碼時潛在的沖突風險(例如:一個像_-Warning-flags_這樣的字符串)。
繼承 - 如何向xcconfig文件的變量中添加值 :
有人成功的向xcconfig文件的變量中添加過新值嗎?
根據其他人對這個問題給出的原因來看,這個值一般不能被繼承。
我們建議的做法是為相對應的變量添加命名空間,在xcconfig文件的末尾加入這一句:
merge.xcconfig:
OTHER_CFLAGS = $(inherited) $(APP_PLATFORM_CFLAGS) $(APP_PROJECT_CFLAGS) $(APP_TARGET_CFLAGS)
另一個方法是利用CocoaPods,它會用自帶的生成器生成特定的xcconfig配置文件,在Pods配置文件中加入:
// Pods/Target\ Support\ Files/Pods/Pods.debug.xcconfig?
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/ObjectiveSugar"
也可以用下面的代替:
// AFNetworking.xcconfig
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public/AFNetworking"
// FinalConfig.xcconfig
#include "AFNetworking.xcconfig"
圖形化 VS 純代碼:鼠標驅動開發 VS 鍵盤驅動開發
例子勝過大篇幅的理論,它們的順序是隨機的,讓我們來猜猜誰是誰:
按下Command + U鍵運行測試用例而不是用鼠標點擊小小的紅色或綠色圖標
在Storyboard中用鼠標設置自動布局而不是用 Carthography 這樣的框架去計算frame
用Makefile或CMake這樣的編譯工具構建靜態庫而不是使用Xcode
打開終端執行Git操作而不是Xcode自帶的源代碼管理工具
是的,在項目初期點幾下鼠標確實能幫我們快速地解決血多簡單的問題,但是隨著我們的系統變得越來越復雜,很難再通過鼠標或觸摸板來管理它。
我們的觀點是:蘋果應該把巨大的人力資源投入到語義開發工具的研發中去(測試框架就是一個好例子),而不是研究那些“具備所有功能”的工具,因為鼠標驅動的開發是不可估量的,而語義開發,永恆的面向對象和SOLID原則都能讓系統變得更易擴展。
目前看起來像蘋果這樣偏愛鼠標驅動的開發者,每當有新的UI特性出現時我們更有可能首先在Xcode中得到新的可點擊的控件,但與之相對應的API卻不幫助我們管理復雜的系統並讓兩者的區別兼容。讓我們再來看一些例子。
常說的自動布局和UI
如果我們看看網絡開發,會發現內容(HTML)和樣式(CSS)在早期被劃分的很明顯。內容和樣式都可以由純文本文件管理的特性啟發蘋果引入了一些基於同樣的原理的工具。我不確定有什麼特定的原因讓蘋果的這些工具的概念和樣式表的概念如此相似,而那時樣式表還沒被引入。
現在一些原生的工具可能對復雜的自動布局處理的不是很好:我們既沒有圖形化的工具為view設置復雜的約束(除非是真正的鼠標點擊者)也沒有蘋果官方研發的細粒度DSL編程語言(這就是為什麼有時候看到成噸的 addConstraint: 代碼時讓我們願意選擇原生工具)。
這就是為什麼如此多的開源解決方案好像開始彌補他們的UI對原生語義支持的不足:ComponentKit和其他的自動布局DSL,像Carthography、Snapkit、Parus都是很好的例子。值得一提的是,到目前為止AFAIK還沒有試圖創建圖形化開發工具,因為沒人想為了鼠標驅動而補做什麼事情。
在我們的文件中不需要Xib,也沒有必要在運行時花費時間處理它們。
用圖形化工具搭建一個界面和用OC/Swift代碼實現同樣的效果,兩者之間的差距之大主要是因為Storyboard與Xib沒辦法讓我們看到實現的中間過程。我們所能得到的最終產物只有在應用程序運的行時空間中生成的像視圖控制器或視圖這樣的二進制文件。
完全替代方法是生成中間代碼的形式類,它基於工廠模式,以便為特定的視圖控制器或視圖從工廠產生的Xib/Storyboard文件。不僅將允許開發人員在編譯過程之前甚至開始運行時觀察中間文件,也會消除編譯時和運行時實例化的開銷。你能想象在應用程序的源文件中沒有Xib/Storyboard的二進制文件嗎,你能期待應用程序在instruments中有更好性能表現嗎?
按這個方向發展最終我們會意識到工廠模式是人性化的,並可能引發一場新的UI編程革命。
XCTest在逐漸丟失測試特征
我使用Cedar測試框架已經有三年了:這個框架 標記規范 。但使用XCTest時,當在標記模式下你想跑一個測試用例或者一個測試類,唯一的辦法是用你的鼠標/觸摸板點擊那個小的綠色/紅色圖標,著啊佯作不僅為代表著敏捷開發的測試驅動開發流增加了額外的復雜性,而且降低開發效率。
我們可以使用像 - (void)ftest...`這樣的規范或者更好的Clang注釋來指示XCTest執行我們標記的測試用例。
結論
說實話,如果有人想聽的話我願意分享更多我對蘋果的觀點。例如,我還沒有寫的第三大集成工具xcodebuild,可能在第二部分又會有一篇文章來談論它。
差不多該結束了:除了上邊給到的例子之外,我們發現了一些在開發中簡單實用的原則:
遠離集成工具。如果你想仔細的思考並且盡可能精細的劃分你的功能模塊,可以使用“高內聚,低耦合”的設計原則。
鼠標驅動型開發無法實現的功能,而有詳細設計類或聲明式編程語言在其中的純文本文件至少有機會做到。