原文地址:http://stanislaw.github.io/2015/11/20/how-apple-could-improve-their-developer-tools.html
如原作者發(fā)現(xiàn)有侵權(quán)行為可責(zé)令我在24小時(shí)之內(nèi)刪除踏施,前提是你能看到秋麸。
蘋果公司如何才能改進(jìn)他們的開(kāi)發(fā)工具
我和我的同事Alex Denisov之前就開(kāi)發(fā)者對(duì)Xcode使用和iOS開(kāi)發(fā)的體驗(yàn)性問(wèn)題進(jìn)行了數(shù)次探討租冠,這篇文章是對(duì)我們談?wù)搩?nèi)容的總結(jié)。
對(duì)于我們研發(fā)出產(chǎn)品最終的性能和Xcode能否提高開(kāi)發(fā)者的工作效率這些問(wèn)題,因?yàn)槲液虯lex Denisov的觀點(diǎn)產(chǎn)生了明顯的分歧急波,所以我非常愿意把我們的觀點(diǎn)分享出來(lái)和大家交流帆卓。
主要有以下幾個(gè)方面:
- 開(kāi)發(fā)環(huán)境的問(wèn)題
- Xcode
- project.pbxproj
- 圖形化和語(yǔ)義的對(duì)比(“鼠標(biāo)驅(qū)動(dòng)的開(kāi)發(fā)”與“鍵盤驅(qū)動(dòng)的開(kāi)發(fā)”)
有諸多問(wèn)題的集成工具為什么還存在至今澜建?
衡量一個(gè)集成開(kāi)發(fā)工具的標(biāo)準(zhǔn)很模糊,換言之可能根本沒(méi)有標(biāo)準(zhǔn)去衡量它:Monoliths are Bad Design… and You Know It蚕甥。
我們都知道闸准,集成開(kāi)發(fā)工具是一些事物從出現(xiàn),成長(zhǎng)到走向成熟的必經(jīng)之路梢灭。但某些觀點(diǎn)認(rèn)為集成開(kāi)發(fā)工具有必要分解成不同功能模塊的組件夷家,這樣每個(gè)組件都可以遵循單一職責(zé)的原則演變并且對(duì)系統(tǒng)的其余部分沒(méi)有影響。按這種角度來(lái)說(shuō)敏释,LLVM就是從GCC集成編譯器中分離出來(lái)的库快,Carthage,“分散依賴?yán)碚摰膭?chuàng)始人”反對(duì)有更多的像CocoaPods這樣的“集成式”工具钥顽,模塊化的AFNetwork 2也是由集成式的AFNetwork 1中演化而來(lái)义屏,這樣的例子還有很多很多。
這有個(gè)例子蜂大,是關(guān)于蘋果公司如何修復(fù)它眾多集成工具之一:Storyboards的闽铐。我們花了好幾年時(shí)間才從蘋果公司得到的“官方”的功能,將巨大的Stroyboard文件分割成一個(gè)一個(gè)更小的single-story-foused奶浦,像RBStoryboardLink這樣的開(kāi)源庫(kù)則被引入的更早兄墅,在2012年:RBStoryboardLink被廢棄了。
除了上面的Storyboards被優(yōu)化的例子澳叉,仍然有很多集成工具隙咸,讓我們看看它們中最有爭(zhēng)議性的一些。
Xcode
顯然Xcode本身就是一個(gè)最大的集成工具:它的主要模塊包括文字編輯器和界面搭建器成洗,其它一些是和編譯設(shè)置管理功能相關(guān)的五督,蘋果賬戶管理,源代碼控制集成等等為什么Xcode糟透了瓶殃。
我們看不到一個(gè)巨大的應(yīng)用做到這一切背后的原因充包,但我們能看到在Xcode7中純代碼文件和xib文件之間的轉(zhuǎn)換從時(shí)間上看可能要花10秒以上,也能看到模擬器的掛起遥椿,奔潰或者重啟(工作一天可能會(huì)看到5次以上的黑屏)基矮。我們從不使用源代碼控制這個(gè)功能,因?yàn)樗鼪](méi)法和SourceTree這樣的專業(yè)的源代碼管理工具相比修壕,甚至是這個(gè)工具僅僅是最普通的命令行愈捅。
目前,越來(lái)越多的人轉(zhuǎn)去使用AppCode慈鸠,一款更加注重代碼編寫的集成開(kāi)發(fā)環(huán)境蓝谨。它并沒(méi)有圖形開(kāi)發(fā)恐懼和其他Xcode內(nèi)嵌的模塊灌具,但卻提供了更好的代碼分析和重構(gòu)工具,我相信譬巫,使用AppCode的開(kāi)發(fā)者有更高的效率咖楣,因?yàn)樗麄兛梢圆挥萌リP(guān)心那些不需要的事情(比如CocoaPods的支持,我認(rèn)為這不是一個(gè)像AppCode這樣“聰明的”IDE所應(yīng)該有的芦昔,有時(shí)候看起來(lái)為了趕時(shí)髦诱贿,某些IDE總想讓自己具備所有的功能。
project.pbxproj
project.pbxproj是我認(rèn)為的僅次于Xcode的第二大集成工具咕缎。
我打賭如果你曾經(jīng)有機(jī)會(huì)把這個(gè)文件從頭讀到尾的話珠十,你一定和我有同樣的印象:這個(gè)文件不適合人類管理,我會(huì)在心里想它的設(shè)計(jì)就是反人類的凭豪。
模棱兩可的標(biāo)識(shí)符在project.pbxproj文件中隨處可見(jiàn) (867CFE661BFFDC5E001F85A8 是一個(gè) /* ViewController.m */正如你所知道的)焙蹭,除了有很糟糕的格式外,還有很多東西是我們所無(wú)法控制的嫂伞。想讓下面這些東西變得可閱讀和可編輯么孔厉,純文本文件是人們很容易理解和維護(hù)的:
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 */ = {),
我們還沒(méi)有設(shè)法讓CMake來(lái)位置iOS的項(xiàng)目結(jié)構(gòu),但是我堅(jiān)信下一個(gè).pbxproj文件的代替者將會(huì)受一些成熟的編譯系統(tǒng)的啟發(fā)帖努,像Make撰豺,CMake,Ninja拼余,Gradle等污桦,他們都基于文本文件,更重要的是它們的設(shè)計(jì)是符合人類的姿搜。
下面我們來(lái)討論一些組織我們舍棄古板的.pbxproj結(jié)構(gòu)的問(wèn)題寡润。
分組vs文件夾
我們根本不需要分組,因?yàn)槊總€(gè)分組總是和對(duì)應(yīng)一個(gè)真實(shí)的文件夾舅柜。甚至可以認(rèn)為分組是反模式化的,如果一個(gè)人用分組來(lái)設(shè)計(jì)的項(xiàng)目結(jié)構(gòu)和真實(shí)文件夾的結(jié)構(gòu)不同的話躲惰,這通常表明他缺乏對(duì)項(xiàng)目真實(shí)文件結(jié)構(gòu)的理解致份,而僅僅關(guān)心的是表面的邏輯結(jié)構(gòu)。
對(duì)這點(diǎn)我早就提到過(guò):我們已經(jīng)準(zhǔn)備開(kāi)始用CMake來(lái)替換掉Xcode iOS的項(xiàng)目架構(gòu)了础拨,并且我們也準(zhǔn)備開(kāi)始用和LLVM的開(kāi)發(fā)者使用的幾乎一樣的CMakeLists.txt的方式來(lái)維護(hù)和開(kāi)發(fā)我們的app的項(xiàng)目架構(gòu)氮块。
我專門去問(wèn)了我的安卓開(kāi)發(fā)同事,他也確認(rèn)在安卓開(kāi)發(fā)中沒(méi)有組的概念诡宗,這樣當(dāng)你向項(xiàng)目中添加一個(gè)新文件夾滔蝉,Git的工作樹(shù)種除了添加那個(gè)文件外其他什么也沒(méi)有,而這一點(diǎn)和Xcode剛好相反:當(dāng)我們像Xcode中添加文件時(shí)塔沃,在project.pbxproj中也會(huì)記錄該文件的入口蝠引,這樣會(huì)導(dǎo)致我們?cè)趍erge/rebase操作時(shí)產(chǎn)生沖突。
xcconfig文件:包裝和繼承
我們有比xcconfig更好的管理和設(shè)置第三方組件,很好地抽象化編譯環(huán)境的配置的工具嗎螃概。
這個(gè)問(wèn)題是我們?cè)?006年提出的矫夯,現(xiàn)在還在這:
自動(dòng)換行:
我寫了一行很長(zhǎng)的代碼,超出了我的屏幕:我怎樣才能將它截?cái)嗟跬荩珻的方法不可行训貌。
你可以在編輯器中打開(kāi)wrapping設(shè)置,但是.xcconfig文件對(duì)自動(dòng)換行支持的并不是很好冒窍。
自動(dòng)換行的特性可以讓代碼更具可讀性并且降低合并代碼時(shí)潛在的沖突風(fēng)險(xiǎn)(例如:一個(gè)像-Warning-flags這樣的字符串)递沪。
繼承 - 如何向xcconfig文件的變量中添加值?:
有人成功的向xcconfig文件的變量中添加過(guò)新值嗎综液?
根據(jù)其他人對(duì)這個(gè)問(wèn)題給出的原因來(lái)看款慨,這個(gè)值一般不能被繼承。
我們建議的做法是為相對(duì)應(yīng)的變量添加命名空間意乓,在xcconfig文件的末尾加入這一句:
merge.xcconfig:
OTHER_CFLAGS = $(inherited) $(APP_PLATFORM_CFLAGS) $(APP_PROJECT_CFLAGS) $(APP_TARGET_CFLAGS)
另一個(gè)方法是利用CocoaPods樱调,它會(huì)用自帶的生成器生成特定的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ū)動(dòng)開(kāi)發(fā) VS 鍵盤驅(qū)動(dòng)開(kāi)發(fā)
舉一些例子勝過(guò)大篇幅的理論届良,它們的順序是隨機(jī)的笆凌,讓我們來(lái)猜猜誰(shuí)是誰(shuí):
- 按下Command + U鍵運(yùn)行測(cè)試用例而不是用鼠標(biāo)點(diǎn)擊小小的紅色或綠色圖標(biāo)
- 在Storyboard中用鼠標(biāo)設(shè)置自動(dòng)布局而不是用Carthography或SnapKit這樣的框架去計(jì)算frame
- 用Makefile或CMake這樣的編譯工具構(gòu)建靜態(tài)庫(kù)而不是使用Xcode
- 打開(kāi)終端執(zhí)行Git操作而不是Xcode自帶的源代碼管理工具
是的,在項(xiàng)目初期點(diǎn)幾下鼠標(biāo)確實(shí)能幫我們快速地解決血多簡(jiǎn)單的問(wèn)題士葫,但是隨著我們的系統(tǒng)變得越來(lái)越復(fù)雜乞而,很難再通過(guò)鼠標(biāo)或觸摸板來(lái)管理它。
我們的觀點(diǎn)是:蘋果應(yīng)該把巨大的人力資源投入到語(yǔ)義開(kāi)發(fā)工具的研發(fā)中去(測(cè)試框架就是一個(gè)好例子)慢显,而不是研究那些“具備所有功能”的工具爪模,因?yàn)槭髽?biāo)驅(qū)動(dòng)的開(kāi)發(fā)是不可估量的,而語(yǔ)義開(kāi)發(fā)荚藻,永恒的面向?qū)ο蠛蚐OLID原則都能讓系統(tǒng)變得更易擴(kuò)展屋灌。
目前看起來(lái)像蘋果這樣偏愛(ài)鼠標(biāo)驅(qū)動(dòng)的開(kāi)發(fā)者,每當(dāng)有新的UI特性出現(xiàn)時(shí)我們更有可能首先在Xcode中得到新的可點(diǎn)擊的控件应狱,但與之相對(duì)應(yīng)的API卻不幫助我們管理復(fù)雜的系統(tǒng)并讓兩者的區(qū)別兼容共郭。讓我們?cè)賮?lái)看一些例子。
常說(shuō)的自動(dòng)布局和UI
如果我們看看網(wǎng)絡(luò)開(kāi)發(fā)疾呻,會(huì)發(fā)現(xiàn)內(nèi)容(HTML)和樣式(CSS)在早期被劃分的很明顯除嘹。內(nèi)容和樣式都可以由純文本文件管理的特性啟發(fā)蘋果引入了一些基于同樣的原理的工具。我不確定有什么特定的原因讓蘋果的這些工具的概念和樣式表的概念如此的相似岸蜗,而那時(shí)樣式表還沒(méi)被引入尉咕。
現(xiàn)在一些原生的工具可能對(duì)復(fù)雜的自動(dòng)布局處理的不是很好:我們既沒(méi)有圖形化的工具為view設(shè)置復(fù)雜的約束(除非是真正的鼠標(biāo)點(diǎn)擊者)也沒(méi)有蘋果官方研發(fā)的細(xì)粒度DSL編程語(yǔ)言(這就是為什么有時(shí)候看到成噸的addConstraint:代碼時(shí)讓我們?cè)敢膺x擇原生工具)。
這就是為什么如此多的開(kāi)源解決方案好像開(kāi)始彌補(bǔ)他們的UI對(duì)原生語(yǔ)義支持的不足:ComponentKit和其他的自動(dòng)布局DSL璃岳,像Carthography年缎、Snapkit悔捶、Parus都是很好的例子。值得一提的是,到目前為止AFAIK還沒(méi)有試圖創(chuàng)建圖形化開(kāi)發(fā)工具晦款,因?yàn)闆](méi)人想為了鼠標(biāo)驅(qū)動(dòng)而補(bǔ)做什么事情炎功。
在我們的文件中不需要Xib,也沒(méi)有必要在運(yùn)行時(shí)花費(fèi)時(shí)間處理它們
用圖形化工具搭建一個(gè)界面和用OC/Swift代碼實(shí)現(xiàn)同樣的效果缓溅,兩者之間的差距之大主要是因?yàn)镾toryboard與Xib沒(méi)辦法讓我們看到實(shí)現(xiàn)的中間過(guò)程蛇损。我們所能得到的最終產(chǎn)物只有在應(yīng)用程序運(yùn)的行時(shí)空間中生成的像視圖控制器或視圖這樣的二進(jìn)制文件。
完全替代方法是生成中間代碼的形式類坛怪,它基于工廠模式,以便為特定的視圖控制器或視圖從工廠產(chǎn)生的Xib/Storyboard文件淤齐。不僅將允許開(kāi)發(fā)人員在編譯過(guò)程之前甚至開(kāi)始運(yùn)行時(shí)觀察中間文件,也會(huì)消除編譯時(shí)和運(yùn)行時(shí)實(shí)例化的開(kāi)銷袜匿。你能想象在應(yīng)用程序的源文件中沒(méi)有Xib/Storyboard的二進(jìn)制文件嗎,你能期待應(yīng)用程序在instruments中有更好性能表現(xiàn)嗎?
按這個(gè)方向發(fā)展最終我們會(huì)意識(shí)到工廠模式是人性化的更啄,并可能引發(fā)一場(chǎng)新的UI編程革命。
XCTest在逐漸丟失測(cè)試特征
我使用Cedar測(cè)試框架已經(jīng)有三年了:這個(gè)框架標(biāo)記規(guī)范居灯。但使用XCTest時(shí)祭务,當(dāng)在標(biāo)記模式下你想跑一個(gè)測(cè)試用例或者一個(gè)測(cè)試類,唯一的辦法是用你的鼠標(biāo)/觸摸板點(diǎn)擊那個(gè)小的綠色/紅色圖標(biāo)怪嫌,著啊佯作不僅為代表著敏捷開(kāi)發(fā)的測(cè)試驅(qū)動(dòng)開(kāi)發(fā)流增加了額外的復(fù)雜性义锥,而且降低開(kāi)發(fā)效率。
我們可以使用像- (void)ftest...
這樣的規(guī)范或者更好的Clang注釋來(lái)指示XCTest執(zhí)行我們標(biāo)記的測(cè)試用例岩灭。
結(jié)論
說(shuō)實(shí)話拌倍,如果有人想聽(tīng)的話我愿意分享更多我對(duì)蘋果的觀點(diǎn)。例如噪径,我還沒(méi)有寫的第三大集成工具xcodebuild柱恤,可能在第二部分又會(huì)有一篇文章來(lái)談?wù)撍?/p>
差不多該結(jié)束了:除了上邊給到的例子之外,我們發(fā)現(xiàn)了一些在開(kāi)發(fā)中簡(jiǎn)單實(shí)用的原則:
遠(yuǎn)離集成工具找爱。如果你想仔細(xì)的思考并且盡可能精細(xì)的劃分你的功能模塊梗顺,可以使用“高內(nèi)聚,低耦合”的設(shè)計(jì)原則车摄。
鼠標(biāo)驅(qū)動(dòng)型開(kāi)發(fā)無(wú)法實(shí)現(xiàn)的功能荚守,而有詳細(xì)設(shè)計(jì)類或聲明式編程語(yǔ)言在其中的純文本文件至少有機(jī)會(huì)做到。