原文地址:http://stanislaw.github.io/2015/11/20/how-apple-could-improve-their-developer-tools.html
如原作者發(fā)現(xiàn)有侵權(quán)行為可責(zé)令我在24小時之內(nèi)刪除,前提是你能看到。
蘋果公司如何才能改進他們的開發(fā)工具
我和我的同事Alex Denisov之前就開發(fā)者對Xcode使用和iOS開發(fā)的體驗性問題進行了數(shù)次探討,這篇文章是對我們談?wù)搩?nèi)容的總結(jié)。
對于我們研發(fā)出產(chǎn)品最終的性能和Xcode能否提高開發(fā)者的工作效率這些問題,因為我和Alex Denisov的觀點產(chǎn)生了明顯的分歧,所以我非常愿意把我們的觀點分享出來和大家交流。
主要有以下幾個方面:
- 開發(fā)環(huán)境的問題
- Xcode
- project.pbxproj
- 圖形化和語義的對比(“鼠標(biāo)驅(qū)動的開發(fā)”與“鍵盤驅(qū)動的開發(fā)”)
有諸多問題的集成工具為什么還存在至今?
衡量一個集成開發(fā)工具的標(biāo)準很模糊,換言之可能根本沒有標(biāo)準去衡量它:Monoliths are Bad Design… and You Know It。
我們都知道,集成開發(fā)工具是一些事物從出現(xiàn),成長到走向成熟的必經(jīng)之路。但某些觀點認為集成開發(fā)工具有必要分解成不同功能模塊的組件,這樣每個組件都可以遵循單一職責(zé)的原則演變并且對系統(tǒng)的其余部分沒有影響。按這種角度來說,LLVM就是從GCC集成編譯器中分離出來的,Carthage,“分散依賴理論的創(chuàng)始人”反對有更多的像CocoaPods這樣的“集成式”工具,模塊化的AFNetwork 2也是由集成式的AFNetwork 1中演化而來,這樣的例子還有很多很多。
這有個例子,是關(guān)于蘋果公司如何修復(fù)它眾多集成工具之一:Storyboards的。我們花了好幾年時間才從蘋果公司得到的“官方”的功能,將巨大的Stroyboard文件分割成一個一個更小的single-story-foused,像RBStoryboardLink這樣的開源庫則被引入的更早,在2012年:RBStoryboardLink被廢棄了。
除了上面的Storyboards被優(yōu)化的例子,仍然有很多集成工具,讓我們看看它們中最有爭議性的一些。
Xcode
顯然Xcode本身就是一個最大的集成工具:它的主要模塊包括文字編輯器和界面搭建器,其它一些是和編譯設(shè)置管理功能相關(guān)的,蘋果賬戶管理,源代碼控制集成等等為什么Xcode糟透了。
我們看不到一個巨大的應(yīng)用做到這一切背后的原因,但我們能看到在Xcode7中純代碼文件和xib文件之間的轉(zhuǎn)換從時間上看可能要花10秒以上,也能看到模擬器的掛起,奔潰或者重啟(工作一天可能會看到5次以上的黑屏)。我們從不使用源代碼控制這個功能,因為它沒法和SourceTree這樣的專業(yè)的源代碼管理工具相比,甚至是這個工具僅僅是最普通的命令行。
目前,越來越多的人轉(zhuǎn)去使用AppCode,一款更加注重代碼編寫的集成開發(fā)環(huán)境。它并沒有圖形開發(fā)恐懼和其他Xcode內(nèi)嵌的模塊,但卻提供了更好的代碼分析和重構(gòu)工具,我相信,使用AppCode的開發(fā)者有更高的效率,因為他們可以不用去關(guān)心那些不需要的事情(比如CocoaPods的支持,我認為這不是一個像AppCode這樣“聰明的”IDE所應(yīng)該有的,有時候看起來為了趕時髦,某些IDE總想讓自己具備所有的功能。
project.pbxproj
project.pbxproj是我認為的僅次于Xcode的第二大集成工具。
我打賭如果你曾經(jīng)有機會把這個文件從頭讀到尾的話,你一定和我有同樣的印象:這個文件不適合人類管理,我會在心里想它的設(shè)計就是反人類的。
模棱兩可的標(biāo)識符在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 */ = {),
我們還沒有設(shè)法讓CMake來位置iOS的項目結(jié)構(gòu),但是我堅信下一個.pbxproj文件的代替者將會受一些成熟的編譯系統(tǒng)的啟發(fā),像Make,CMake,Ninja,Gradle等,他們都基于文本文件,更重要的是它們的設(shè)計是符合人類的。
下面我們來討論一些組織我們舍棄古板的.pbxproj結(jié)構(gòu)的問題。
分組vs文件夾
我們根本不需要分組,因為每個分組總是和對應(yīng)一個真實的文件夾。甚至可以認為分組是反模式化的,如果一個人用分組來設(shè)計的項目結(jié)構(gòu)和真實文件夾的結(jié)構(gòu)不同的話,這通常表明他缺乏對項目真實文件結(jié)構(gòu)的理解,而僅僅關(guān)心的是表面的邏輯結(jié)構(gòu)。
對這點我早就提到過:我們已經(jīng)準備開始用CMake來替換掉Xcode iOS的項目架構(gòu)了,并且我們也準備開始用和LLVM的開發(fā)者使用的幾乎一樣的CMakeLists.txt的方式來維護和開發(fā)我們的app的項目架構(gòu)。
我專門去問了我的安卓開發(fā)同事,他也確認在安卓開發(fā)中沒有組的概念,這樣當(dāng)你向項目中添加一個新文件夾,Git的工作樹種除了添加那個文件外其他什么也沒有,而這一點和Xcode剛好相反:當(dāng)我們像Xcode中添加文件時,在project.pbxproj中也會記錄該文件的入口,這樣會導(dǎo)致我們在merge/rebase操作時產(chǎn)生沖突。
xcconfig文件:包裝和繼承
我們有比xcconfig更好的管理和設(shè)置第三方組件,很好地抽象化編譯環(huán)境的配置的工具嗎。
這個問題是我們在2006年提出的,現(xiàn)在還在這:
自動換行:
我寫了一行很長的代碼,超出了我的屏幕:我怎樣才能將它截斷,C的方法不可行。
你可以在編輯器中打開wrapping設(shè)置,但是.xcconfig文件對自動換行支持的并不是很好。
自動換行的特性可以讓代碼更具可讀性并且降低合并代碼時潛在的沖突風(fēng)險(例如:一個像-Warning-flags這樣的字符串)。
繼承 - 如何向xcconfig文件的變量中添加值?:
有人成功的向xcconfig文件的變量中添加過新值嗎?
根據(jù)其他人對這個問題給出的原因來看,這個值一般不能被繼承。
我們建議的做法是為相對應(yīng)的變量添加命名空間,在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 純代碼:鼠標(biāo)驅(qū)動開發(fā) VS 鍵盤驅(qū)動開發(fā)
舉一些例子勝過大篇幅的理論,它們的順序是隨機的,讓我們來猜猜誰是誰:
- 按下Command + U鍵運行測試用例而不是用鼠標(biāo)點擊小小的紅色或綠色圖標(biāo)
- 在Storyboard中用鼠標(biāo)設(shè)置自動布局而不是用Carthography或SnapKit這樣的框架去計算frame
- 用Makefile或CMake這樣的編譯工具構(gòu)建靜態(tài)庫而不是使用Xcode
- 打開終端執(zhí)行Git操作而不是Xcode自帶的源代碼管理工具
是的,在項目初期點幾下鼠標(biāo)確實能幫我們快速地解決血多簡單的問題,但是隨著我們的系統(tǒng)變得越來越復(fù)雜,很難再通過鼠標(biāo)或觸摸板來管理它。
我們的觀點是:蘋果應(yīng)該把巨大的人力資源投入到語義開發(fā)工具的研發(fā)中去(測試框架就是一個好例子),而不是研究那些“具備所有功能”的工具,因為鼠標(biāo)驅(qū)動的開發(fā)是不可估量的,而語義開發(fā),永恒的面向?qū)ο蠛蚐OLID原則都能讓系統(tǒng)變得更易擴展。
目前看起來像蘋果這樣偏愛鼠標(biāo)驅(qū)動的開發(fā)者,每當(dāng)有新的UI特性出現(xiàn)時我們更有可能首先在Xcode中得到新的可點擊的控件,但與之相對應(yīng)的API卻不幫助我們管理復(fù)雜的系統(tǒng)并讓兩者的區(qū)別兼容。讓我們再來看一些例子。
常說的自動布局和UI
如果我們看看網(wǎng)絡(luò)開發(fā),會發(fā)現(xiàn)內(nèi)容(HTML)和樣式(CSS)在早期被劃分的很明顯。內(nèi)容和樣式都可以由純文本文件管理的特性啟發(fā)蘋果引入了一些基于同樣的原理的工具。我不確定有什么特定的原因讓蘋果的這些工具的概念和樣式表的概念如此的相似,而那時樣式表還沒被引入。
現(xiàn)在一些原生的工具可能對復(fù)雜的自動布局處理的不是很好:我們既沒有圖形化的工具為view設(shè)置復(fù)雜的約束(除非是真正的鼠標(biāo)點擊者)也沒有蘋果官方研發(fā)的細粒度DSL編程語言(這就是為什么有時候看到成噸的addConstraint:代碼時讓我們愿意選擇原生工具)。
這就是為什么如此多的開源解決方案好像開始彌補他們的UI對原生語義支持的不足:ComponentKit和其他的自動布局DSL,像Carthography、Snapkit、Parus都是很好的例子。值得一提的是,到目前為止AFAIK還沒有試圖創(chuàng)建圖形化開發(fā)工具,因為沒人想為了鼠標(biāo)驅(qū)動而補做什么事情。
在我們的文件中不需要Xib,也沒有必要在運行時花費時間處理它們
用圖形化工具搭建一個界面和用OC/Swift代碼實現(xiàn)同樣的效果,兩者之間的差距之大主要是因為Storyboard與Xib沒辦法讓我們看到實現(xiàn)的中間過程。我們所能得到的最終產(chǎn)物只有在應(yīng)用程序運的行時空間中生成的像視圖控制器或視圖這樣的二進制文件。
完全替代方法是生成中間代碼的形式類,它基于工廠模式,以便為特定的視圖控制器或視圖從工廠產(chǎn)生的Xib/Storyboard文件。不僅將允許開發(fā)人員在編譯過程之前甚至開始運行時觀察中間文件,也會消除編譯時和運行時實例化的開銷。你能想象在應(yīng)用程序的源文件中沒有Xib/Storyboard的二進制文件嗎,你能期待應(yīng)用程序在instruments中有更好性能表現(xiàn)嗎?
按這個方向發(fā)展最終我們會意識到工廠模式是人性化的,并可能引發(fā)一場新的UI編程革命。
XCTest在逐漸丟失測試特征
我使用Cedar測試框架已經(jīng)有三年了:這個框架標(biāo)記規(guī)范。但使用XCTest時,當(dāng)在標(biāo)記模式下你想跑一個測試用例或者一個測試類,唯一的辦法是用你的鼠標(biāo)/觸摸板點擊那個小的綠色/紅色圖標(biāo),著啊佯作不僅為代表著敏捷開發(fā)的測試驅(qū)動開發(fā)流增加了額外的復(fù)雜性,而且降低開發(fā)效率。
我們可以使用像- (void)ftest...
這樣的規(guī)范或者更好的Clang注釋來指示XCTest執(zhí)行我們標(biāo)記的測試用例。
結(jié)論
說實話,如果有人想聽的話我愿意分享更多我對蘋果的觀點。例如,我還沒有寫的第三大集成工具xcodebuild,可能在第二部分又會有一篇文章來談?wù)撍?/p>
差不多該結(jié)束了:除了上邊給到的例子之外,我們發(fā)現(xiàn)了一些在開發(fā)中簡單實用的原則:
遠離集成工具。如果你想仔細的思考并且盡可能精細的劃分你的功能模塊,可以使用“高內(nèi)聚,低耦合”的設(shè)計原則。
鼠標(biāo)驅(qū)動型開發(fā)無法實現(xiàn)的功能,而有詳細設(shè)計類或聲明式編程語言在其中的純文本文件至少有機會做到。