深入谈一谈iOS模块独立运行
背景
最近一直在團隊推進關(guān)于iOS模塊獨立運行相關(guān)的事項,想把最近的一些想法和實施情況通過這篇文章做一個記錄。
如果在一個項目中,某一塊代碼足夠獨立(功能、業(yè)務(wù)上),就會傾向于將他通過Cocoapods抽離為一個pods文件。通過一個podspec文件描述這個pod的信息。
最直接的方式就是把相關(guān)文件組織好遷移到一個目錄下,通過podspec對源碼位置,資源位置等信息的描述,完成一個最簡單直白的pod創(chuàng)建。
并且在Podfile中通通過
pod 'XXX', :path => '~/dev/XXX' 復(fù)制代碼這種方式,集成到主工程進行開發(fā)。開發(fā)自測完成后通過私有repo發(fā)布,并且將Podfile中的指向版本
pod 'XXX', '1.0.0' 復(fù)制代碼然后我們就會說,抽離了一個庫,項目的代碼結(jié)構(gòu)變得更合理更清晰了。
但這么做,在我看來跟在項目里直接用group把這些代碼區(qū)別開來沒什么區(qū)別,反而要修改的時候還要重新git pull、pod install變得更加麻煩了。
因為這么做被分離的代碼無法獨立運行,而且由于依賴不清晰,沒辦法共享給其他項目使用,導(dǎo)致這種分離方式是一種偽解耦,該有的益處沒有展現(xiàn)出來,修改的時候反倒更加麻煩,這絕對不是我們想要的效果。
由此引出對模塊獨立運行
對iOS模塊獨立運行的思考
個人心目中完美的Pod應(yīng)該在這四個維度上做到最好。
-
依賴足夠清晰 清楚的描述自己的依賴狀況。說到這個不得不吐槽一個iOS項目與Cocoapods結(jié)合之后一個奇怪的現(xiàn)象,就是Pod如果在主工程中通過path引入的,那么,在不聲明清楚自身以來的情況下,可以使用主工程內(nèi)所有類,甚至是主工程的類,并且不會得到任何提示,這種情況很普遍,并且事后想要理清依賴的成本極高,這個Pod算是廢了(沒有了抽離Pod的意義了)。
-
方便共享給其他項目使用 我的理解,抽離Pod的很大一個目的不就是為了共享嗎?如果不是為了這個目的,其實沒必要抽離Pod的,反而更加麻煩了,只跟一個項目綁死的Pod在我看來,完全沒有必要做成Pod,項目里放在Group就可以了。而為了能達到這個目的需要克服很多的困難。
-
方便快速修改驗證 隨著主工程越來越大,編譯速度越來越慢,開發(fā)效率也在無形之中慢慢降低。Pod從主工程中脫離出來獨立運行,單獨編譯,隔絕對主工程的依賴,完全自給自足,這樣代碼編譯量就會大大降低,達到開發(fā)效能提高的目的。
-
自身質(zhì)量保障 在第三點的基礎(chǔ)上,帶上完全針對這個Pod的單元測試UI測試,完美顆粒化代碼的同時,還能很好的保障自身的質(zhì)量,并且清晰易維護,這塊如果配合Xcode Server,會發(fā)揮強大的優(yōu)勢。
然而要做到這些維度上的最好,需要克服很多問題,接下來慢慢道來。
兩種類型的代碼
類似AFNetworking、SDWebImage這樣的功能型代碼,分離的一個只需要確保自己的依賴清晰,被依賴的時候使用方便就可以了。而對于偏業(yè)務(wù)型的代碼就不那么容易了,通常會有界面,還會有各種業(yè)務(wù)帶來的附加產(chǎn)物,例如打點,例如網(wǎng)絡(luò)庫各種規(guī)則。
因為功能型代碼本身對于以上四點門檻不高,我就不展開討論了,主要還是展開說一下業(yè)務(wù)型代碼。
業(yè)務(wù)型代碼獨立運行的看法
推進業(yè)務(wù)型代碼獨立運行過程中遇到的一些問題列舉:
業(yè)務(wù)代碼需要用到 打點、網(wǎng)絡(luò)等基本能力,背后每個能力都可能牽扯出一堆間接依賴,但這些依賴跟這個Pod本身沒任何屁關(guān)系,同時還會是不是的出現(xiàn)間接依賴不明確導(dǎo)致的編譯報錯問題。
作為已經(jīng)是獨立可運行的Pod了,界面什么的都自己hold了,那么它一定還需要跟其他本身之外的幾面進行交互,舉個例子,一個Product的Pod,需要跳轉(zhuǎn)到Order中的一個界面,或者Chat中的一個界面,而這個界面在代碼層面根本不存在,要如何處置。
之前也提到了,一個依賴清晰的獨立運行Pod如果被不小心path方式開發(fā)了一次,那么這個Pod會慢慢變廢,下次運行可能就不能運行了,所以還要想辦法要怎么不被path依賴。
還有一個比較頭疼的問題是,隨著業(yè)務(wù)迭代,某個冷門的獨立運行Pod并沒有跟上腳步,其直接依賴的功能庫在主工程都更新了,但它卻全然不知,難道還要一個一個校對嗎?
解決了基礎(chǔ)能力的間接依賴,各種必要的直接依賴的間接依賴也會出現(xiàn)不明確而出現(xiàn)的編譯失敗問題,需要解決,確保只有代碼api需要更新時才會編譯不過,才是最爽的開發(fā)流程。
思考實施解決之道
實施過程中開發(fā)了兩套工具來解決。均未開源,外網(wǎng)勿搜。下面介紹詳細思路。
腳手架工具 - gearmaker
避免被path模式開發(fā)
好不容建了一個獨立運行Pod,要是不小心被不明真相的同學(xué)用path開發(fā)了就糟了。如何避免被path模式開發(fā)呢?如果是源碼模式下,其實是做不到的。所以我們把每個獨立運行的Pod的產(chǎn)物定位二進制庫,靜態(tài)動態(tài)都可,podspec層面就不允許指向源碼,想要修改源碼,只能通過獨立運行的工程進行修改。
具體操作這里不展開了,如果podspec指向的是靜態(tài)庫,而沒有源碼指向則這個Pod理論上不可能不可被path模式開發(fā)。
而如果通過Cocoapods 官方建議的 pod lib create 方式創(chuàng)建,則Pod代碼會存在于 Development Pods 下,而必須在podspec中指定源碼路徑,因此我修改了 pod lib create 的腳手架模板,將Pod代碼直接放入項目的一個Group中,而Group對應(yīng)產(chǎn)物是一個framework,podspec直接指向podspec,外加Universal打包腳本就可以啦。
有同學(xué)有疑問了,那源碼調(diào)試怎么辦呢?這個不用擔心,既然Pod已經(jīng)可以獨立運行,有什么問題需要調(diào)試,是都可以在Pod工程中進行數(shù)據(jù)Mock來還原問題的,所以主工程只是用來集成,不需要考慮調(diào)試問題。
即便是特別特殊的情況,只在主工程能還原,那臨時加一下podspec指向本地path做一下debug也是可以的。
版本仲裁 & 保鮮
Pod獨立運行工程的一大詬病就是時間一長,工程就無法編譯通過運行了,并且哪些依賴需要更新,需要詳細對比,成本非常高,很多遇到這樣情況就會放棄Pod工程。
為此gearmaker中集成了版本仲裁能力,通過hook pod命令,在pod install之前,計算出指定客戶端主工程最新的依賴全集,在pod install時,在這個全集中找到仲裁版本來使用。
這樣一來,pod install后的Pod工程所有依賴必然與主工程一致,只需要修改因為依賴更新帶來的相關(guān)api變更即可通過編譯正常運行,確保Pod工程不會腐敗。
依賴切斷 服務(wù)提供組件 - ServiceProvider
所有Pod只需要直接依賴ServiceProvider,由ServiceProvider來統(tǒng)一提供服務(wù)能力,包括Pod工程本身需要的任何能力。比如,路由、打點、網(wǎng)絡(luò)、從另一個模塊獲取數(shù)據(jù),獲取一個View對象等,均不需要依賴其他庫,直接從ServiceProvider中通過內(nèi)置的protocol來獲得,并使用。
提供能力的一方對預(yù)先放置在ServiceProvider中的protocol進行功能實現(xiàn),通過以下方式將自身的能力注冊入ServiceProvider,即可為其他提供能力,而不需要依賴。
注冊服務(wù)
[ServiceProvider registService:[XMUserTrack class] withProtocol:@protocol(UserTrack)]; 復(fù)制代碼獲取服務(wù)
id<UserTrack> ut = [ServiceProvider serviceWithProtocol:@protocol(UserTrack)]; 復(fù)制代碼使用體驗
對于新的Pod創(chuàng)建,直接使用以下命令創(chuàng)建:
gearmaker <PodName> 復(fù)制代碼根據(jù)命令行提示進行創(chuàng)建即可。創(chuàng)建完成的腳手架直接提供了ServiceProvider,開發(fā)同學(xué)直接從ServiceProvider中獲取服務(wù)進行Pod開發(fā),開發(fā)完成后通過Universal腳本生成framework上傳到私有repo中定版本即可直接使用。
總結(jié)
以上是生活随笔為你收集整理的深入谈一谈iOS模块独立运行的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出C/C++中的正则表达式库(二)
- 下一篇: 干货!9种高性能可用高并发的技术架构