iOS MVVM最佳實(shí)踐(一)

引言

??沒有代碼的瞎掰都是耍流氓!
??這里是我使用MVVM模式結(jié)合組件化所做的音樂類App小部分功能,包括歌單、搜索和播放。你可以在這里看到viewModel到底如何書寫、如何與controller配合,組件化應(yīng)該如何搭建等等細(xì)節(jié)。麻雀雖小五臟俱全,作為參照來講應(yīng)該是足夠了的。
??這個(gè)系列是我在實(shí)踐中的一些總結(jié)。學(xué)習(xí)之路上翻閱了不少博客,卻都是介紹概念的居多,幾乎沒見著有完整代碼供后來的人參考的。我遇到了很多坑也跨過了很多坑,所以有了寫這幾篇博客的動(dòng)機(jī)。這幾篇博客是配合我的代碼來的,闡述了我是怎么做的、我為什么要這么做,當(dāng)然我的做法不見得都是對(duì)的,只是希望能借此拋磚引玉。
??當(dāng)然啦,基本的理論基礎(chǔ)還是要有的,這第一篇先簡單介紹一下。

什么是MVVM

??隨著ReactiveCocoaRxSwift的橫空出世,MVVM,這一發(fā)源于微軟的軟件架構(gòu)模式也大熱了起來,眾多iOSer對(duì)此有著大量的討論。
??我們先來看一眼MVC,Apple推薦的iOS App標(biāo)準(zhǔn)架構(gòu)模式。

取自斯坦福公開課.png

??view將用戶請(qǐng)求通知給controller,controller通過更新model來反應(yīng)狀態(tài)的改變,model(使用KVO和Notification)通知controller來更新他們負(fù)責(zé)的view。然而實(shí)際開發(fā)中充斥著大量如下代碼:

class MyView: UIView {
  var model: MyModel? {
    didSet {
      // 在這里更新view的狀態(tài)
    }
  } 
}

如此一來view和model就綁定在了一起,形成了緊耦合也就沒有復(fù)用一說了。奇怪的是這種寫法是那么的自然以至于大家都沒有感覺到有什么不對(duì)勁~~
??再來看看MVVM:


取自微軟MVVM介紹.png

MVVM是對(duì)MVC的一種改進(jìn)。它將view和controller都視作view,view不持有model而是和viewModel通信,model的持有者變成了viewModel。在這個(gè)架構(gòu)中viewModel接收用戶請(qǐng)求處理業(yè)務(wù)邏輯并更新model,然后再將處理結(jié)果(也就是對(duì)model的更新)通過通知傳回view來同步狀態(tài)。使用MVVM能很好的分離UI和業(yè)務(wù)邏輯,view不持有model使得復(fù)用變成可能,viewModel因?yàn)椴缓魏蜺I元素因而能和任何view組合使用,同時(shí)對(duì)viewModel的測試也相對(duì)變得簡單。
??MVVM是兼容于MVC的,你可以在項(xiàng)目中既使用MVC又引入MVVM,它對(duì)現(xiàn)有的代碼并沒有任何侵入性。所以少年,如果你還沒有使用過MVVM,趕緊去試試吧,就像Swift一樣,一旦上手了以后就再也回不去Objective-C了~~

Rx系列和MVVM的關(guān)系

??為什么Rx系列出現(xiàn)之前很少聽說MVVM?Rx系列出現(xiàn)之后MVVM就受到如此推崇?難道說沒有Rx系列的支持就無法使用MVVM?或者說MVVM是專門為Rx系列設(shè)計(jì)的?相信大家或多或少有著這樣的疑問,這里推薦閱讀iOS 關(guān)于MVVM Without ReactiveCocoa設(shè)計(jì)模式的那些事。
??簡而言之,iOS中可以使用Notification,KVO來實(shí)現(xiàn)綁定,只是過程比較痛苦(KVO不僅繁瑣且回調(diào)略顯蛋疼),而反觀Rx系列,它使用觀察者模式:

  • 可以方便的創(chuàng)建事件流和數(shù)據(jù)流
  • 使用查詢式的操作符組合和變換數(shù)據(jù)流
  • 可以訂閱任何可觀察的數(shù)據(jù)流并執(zhí)行操作

這就使得數(shù)據(jù)綁定變得極其簡單。正是因?yàn)镽x系列完美契合于MVVM模式,所以才導(dǎo)致MVVM模式的大熱。RxSwift和ReactiveCocoa因?yàn)楦叨阮愃疲莆掌渲幸环N足以。我這邊使用的是RxSwift,這里安利兩篇文檔給不熟悉的小伙伴們:

什么是組件化開發(fā)

??一般我們?cè)陂_發(fā)的時(shí)候都是新建一個(gè)Cocoa Touch Application,然后根據(jù)功能模塊劃分幾個(gè)文件夾,比如Home/Discover/Profile/Vendor等等,這些文件夾又各自有著Controller/Model/Util之類的分層??雌饋硪呀?jīng)劃分了功能模塊,實(shí)際上這卻是一個(gè)復(fù)雜的項(xiàng)目。所有的代碼都在一個(gè)單一工程里,久而久之尤其是多人開發(fā)的時(shí)候,耦合會(huì)特別嚴(yán)重,你會(huì)發(fā)現(xiàn)去掉任何一個(gè)模塊的代碼,整個(gè)項(xiàng)目完全無法運(yùn)行,并且即使你只想測試某個(gè)功能也不得不編譯整個(gè)項(xiàng)目,組件化因此應(yīng)運(yùn)而生。
??對(duì)于組件化開發(fā),我的理解就是將一個(gè)龐大臃腫的單一工程, 根據(jù)功能或者屬性進(jìn)行分解,拆分成各個(gè)獨(dú)立的模塊或者說是組件,這一步和之前其實(shí)沒有區(qū)別,區(qū)別在于每一個(gè)組件都可以單獨(dú)維護(hù)、獨(dú)立運(yùn)行,然后可以根據(jù)業(yè)務(wù)需求,組織成一個(gè)擁有完整業(yè)務(wù)邏輯的工程。;組件化特別適合大型項(xiàng)目的開發(fā),因?yàn)椋?/p>

  • 便捷性:僅僅通過改變依賴就可以進(jìn)行功能的增減
  • 獨(dú)立性:每個(gè)組件可以單獨(dú)編寫、單獨(dú)編譯、單獨(dú)運(yùn)行和單獨(dú)測試
  • 復(fù)用性:對(duì)于功能性,工具性的代碼亦或是通用的控件等等,可以很輕松的復(fù)用

如何組件化

??組件化帶來的好處是非常直觀的,并且它實(shí)現(xiàn)起來并不復(fù)雜,使用CocoaPods可以很方便的實(shí)現(xiàn)組件化。關(guān)于CocoaPods的基本使用相信大家都是十分熟悉的,這里就不介紹了。

pod lib create #ModuleName#

使用以上命令就可以在本地新建一個(gè)pod庫。組件編寫完以后你可以上傳到遠(yuǎn)程服務(wù)器或者僅僅當(dāng)作本地私有庫使用。這里面唯一的坑點(diǎn)是集成第三方靜態(tài)庫,比如項(xiàng)目中使用到極光分享,在podsepc中寫下:

s.dependency 'JShare'

會(huì)發(fā)現(xiàn)CocoaPods報(bào)錯(cuò)。

The 'Pods-xx' target has transitive dependencies that include static binaries

解決的辦法在這里。簡單來說就是我們新建一個(gè)Cocoa Touch Framework來包裝這個(gè)靜態(tài)庫就可以。如果你也遇到了這個(gè)問題,這篇文章應(yīng)該能夠幫到你。

組件化中間層如何實(shí)現(xiàn)

??設(shè)計(jì)組件化中間層有兩種比較有代表性的方案:

??兩種方式我都有嘗試過,談一點(diǎn)個(gè)人看法吧。URL注冊(cè)的方式在使用上非常繁瑣而且很多時(shí)候其實(shí)沒有必要。首先每一個(gè)頁面跳轉(zhuǎn)都需要事先注冊(cè)好URL,這里會(huì)牽涉到非常多字符串硬編碼,很容易出錯(cuò);其次兩個(gè)有強(qiáng)關(guān)聯(lián)的頁面,比如從預(yù)覽到詳情,這兩個(gè)頁面總是會(huì)成對(duì)出現(xiàn),你幾乎沒有其他選擇會(huì)跳轉(zhuǎn)其他頁面,這種情況下個(gè)人感覺注冊(cè)不注冊(cè)URL都沒有什么關(guān)系?;趓untime的Mediator方式,首先它不需要注冊(cè),省去了很多比對(duì)字符串的過程,當(dāng)然,這種方式依然避免不了字符串硬編碼,不過體量相對(duì)較??;其次它可以非常容易的傳遞各種參數(shù)來進(jìn)行組建間通信。
??我選擇的是Mediator方式,也因?yàn)樗子趯?shí)現(xiàn),在模塊的劃分上我的做法粒度比較大,這些具體在代碼里體現(xiàn)。

總結(jié)

??以上簡要介紹了MVVM和組件化開發(fā),我這里就說這么多了,接下來是show you the code環(huán)節(jié)。需要說明的是這是一份Swift代碼,并且使用了RxSwift,如果你對(duì)這些都不熟悉,可能這個(gè)暫時(shí)還不太適合你。使用ReactiveCocoa的同學(xué)不要慌,首先RxSwift和ReactiveCocoa區(qū)別真沒那么大,再者這里講的是如何做,和使用的庫關(guān)系不大,套用一下即可。
??下一篇講的是代碼邏輯,喜歡看干貨的同學(xué)不要錯(cuò)過了喲。這里再次安利一下代碼地址,如果覺得有幫助,點(diǎn)個(gè)star唄,又不會(huì)懷孕不是~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容