?一、MVC
從字面意思來理解,MVC 即 Model View Controller(模型 視圖 控制器),是 Xerox PARC 在 20 世紀(jì) 80 年代為編程語言 Smalltalk-80 發(fā)明的一種軟件設(shè)計模式,至今已廣泛應(yīng)用于用戶交互應(yīng)用程序中。其用意在于將數(shù)據(jù)與視圖分離開來。在 iOS 開發(fā)中 MVC 的機(jī)制被使用的淋漓盡致,充分理解 iOS 的 MVC 模式,有助于我們程序的組織合理性。
MVC 的幾個明顯的特征和體現(xiàn):
View 上面顯示什么東西,取決于 Model。
只要 Model 數(shù)據(jù)改了,View 的顯示狀態(tài)會跟著更改。
Control 負(fù)責(zé)初始化 Model,并將 Model 傳遞給 View 去解析展示。
1)Model模型對象:
模型對象封裝了應(yīng)用程序的數(shù)據(jù),并定義操控和處理該數(shù)據(jù)的邏輯和運(yùn)算。例如,模型對象可能是表示商品數(shù)據(jù) list。用戶在視圖層中所進(jìn)行的創(chuàng)建或修改數(shù)據(jù)的操作,通過控制器對象傳達(dá)出去,最終會創(chuàng)建或更新模型對象。模型對象更改時(例如通過網(wǎng)絡(luò)連接接收到新數(shù)據(jù)),它通知控制器對象,控制器對象更新相應(yīng)的視圖對象。
2)View 視圖對象:
視圖對象是應(yīng)用程序中用戶可以看見的對象。視圖對象知道如何將自己繪制出來,可能對用戶的操作作出響應(yīng)。視圖對象的主要目的就是顯示來自應(yīng)用程序模型對象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯。盡管如此,在 MVC 應(yīng)用程序中,視圖對象通常與模型對象分離。
在iOS應(yīng)用程序開發(fā)中,所有的控件、窗口等都繼承自 UIView,對應(yīng) MVC 中的 V。UIView 及其子類主要負(fù)責(zé) UI 的實現(xiàn),而 UIView 所產(chǎn)生的事件都可以采用委托的方式,交給 UIViewController 實現(xiàn)。
3)Controller 控制器對象:
在應(yīng)用程序的一個或多個視圖對象和一個或多個模型對象之間,控制器對象充當(dāng)媒介??刂破鲗ο笠虼耸峭焦艿莱绦颍ㄟ^它,視圖對象了解模型對象的更改,反之亦然。控制器對象還可以為應(yīng)用程序執(zhí)行設(shè)置和協(xié)調(diào)任務(wù),并管理其他對象的生命周期。
控制器對象解釋在視圖對象中進(jìn)行的用戶操作,并將新的或更改過的數(shù)據(jù)傳達(dá)給模型對象。模型對象更改時,一個控制器對象會將新的模型數(shù)據(jù)傳達(dá)給視圖對象,以便視圖對象可以顯示它。
對于不同的 UIView,有相應(yīng)的 UIViewController,對應(yīng) MVC 中的 C。例如在 iOS 上常用的 UITableView,它所對應(yīng)的 Controller 就是UITableViewController。
iOS MVC 示意圖

1)Model 和 View 永遠(yuǎn)不能相互通信,只能通過 Controller 傳遞。
2)Controller 可以直接與 Model 對話(讀寫調(diào)用 Model),Model 通過 Notification 和 KVO 機(jī)制與 Controller 間接通信。
3)Controller 可以直接與 View 對話,通過 outlet,直接操作 View,outlet 直接對應(yīng)到 View 中的控件,View 通過 action 向 Controller 報告事件的發(fā)生(如用戶 Touch 我了)。Controller 是 View 的直接數(shù)據(jù)源(數(shù)據(jù)很可能是 Controller 從 Model 中取得并經(jīng)過加工了)。Controller 是 View 的代理(delegate),以同步 View 與 Controller。
MVC自身不足
1)MVC 在現(xiàn)實應(yīng)用中的不足:
在 MVC 模式中 view 將用戶交互通知給控制器。view 的控制器通過更新 Model 來反應(yīng)狀態(tài)的改變。Model(通常使用 Key-Value-Observation)通知控制器來更新他們負(fù)責(zé)的 view。大多數(shù) iOS 應(yīng)用程序的代碼使用這種方式來組織。
2)愈發(fā)笨重的 Controller:
在傳統(tǒng)的 app 中模型數(shù)據(jù)一般都很簡單,不涉及到復(fù)雜的業(yè)務(wù)數(shù)據(jù)邏輯處理,客戶端開發(fā)受限于它自身運(yùn)行的的平臺終端,這一點(diǎn)注定使移動端不像 PC 前端那樣能夠處理大量的復(fù)雜的業(yè)務(wù)場景。然而隨著移動平臺的各種深入,我們不得不考慮這個問題。傳統(tǒng)的 Model 數(shù)據(jù)大多來源于網(wǎng)絡(luò)數(shù)據(jù),拿到網(wǎng)絡(luò)數(shù)據(jù)后客戶端要做的事情就是將數(shù)據(jù)直接按照順序畫在界面上。隨著業(yè)務(wù)的越來越來的深入,我們依賴的 service 服務(wù)可能在大多時間無法第一時間滿足客戶端需要的數(shù)據(jù)需求,移動端愈發(fā)的要自行處理一部分邏輯計算操作。這個時間一慣的做法是在控制器中處理,最終導(dǎo)致了控制器成了垃圾箱,越來越不可維護(hù)。
控制器 Controller 是 app 的 “膠水代碼”,協(xié)調(diào)模型和視圖之間的所有交互??刂破髫?fù)責(zé)管理他們所擁有的視圖的視圖層次結(jié)構(gòu),還要響應(yīng)視圖的 loading、appearing、disappearing 等等,同時往往也會充滿我們不愿暴露的 Model 的模型邏輯以及不愿暴露給視圖的業(yè)務(wù)邏輯。這引出了第一個關(guān)于 MVC 的問題...
視圖 view 通常是 UIKit 控件(component,這里根據(jù)習(xí)慣譯為控件)或者編碼定義的 UIKit 控件的集合。進(jìn)入 .xib 或者 Storyboard 會發(fā)現(xiàn)一個 app、Button、Label 都是由這些可視化的和可交互的控件組成。View 不應(yīng)該直接引用 Model,并且僅僅通過 IBAction 事件引用 controller。業(yè)務(wù)邏輯很明顯不歸入 view,視圖本身沒有任何業(yè)務(wù)。
厚重的 View Controller 由于大量的代碼被放進(jìn) viewcontroller,導(dǎo)致他們變的相當(dāng)臃腫。在 iOS 中有的 view controller 里綿延成千上萬行代碼的事并不是前所未見的。這些超重 app 的突出情況包括:厚重的 View Controller 很難維護(hù)(由于其龐大的規(guī)模);包含幾十個屬性,使他們的狀態(tài)難以管理;遵循許多協(xié)議(protocol),導(dǎo)致協(xié)議的響應(yīng)代碼和 controller 的邏輯代碼混淆在一起。
厚重的 view controller 很難測試,不管是手動測試或是使用單元測試,因為有太多可能的狀態(tài)。將代碼分解成更小的多個模塊通常是件好事。
3)太過于輕量級的 Model:
早期的 Model 層,其實就是如果數(shù)據(jù)有幾個屬性,就定義幾個屬性,ARC 普及以后我們在 Model 層的實現(xiàn)文件中基本上看不到代碼(無需再手動管理釋放變量,Model 既沒有復(fù)雜的業(yè)務(wù)處理,也沒有對象的構(gòu)造,基本上 .m 文件中的代碼普遍是空的);同時與控制器的代碼越來厚重形成強(qiáng)烈的反差,這一度讓人不禁對現(xiàn)有的開發(fā)設(shè)計構(gòu)思有所懷疑。
4)遺失的網(wǎng)絡(luò)邏輯:
蘋果使用的 MVC 的定義是這么說的:所有的對象都可以被歸類為一個 Model,一個 view,或是一個控制器。就這些,那么把網(wǎng)絡(luò)代碼放哪里?和一個 API 通信的代碼應(yīng)該放在哪兒?
你可能試著把它放在 Model 對象里,但是也會很棘手,因為網(wǎng)絡(luò)調(diào)用應(yīng)該使用異步,這樣如果一個網(wǎng)絡(luò)請求比持有它的 Model 生命周期更長,事情將變的復(fù)雜。顯然也不應(yīng)該把網(wǎng)絡(luò)代碼放在 view 里,因此只剩下控制器了。這同樣是個壞主意,因為這加劇了厚重控制器的問題。那么應(yīng)該放在那里呢?顯然 MVC 的 3 大組件根本沒有適合放這些代碼的地方。
5)較差的可測試性:
MVC 的另一個大問題是,它不鼓勵開發(fā)人員編寫單元測試。由于控制器混合了視圖處理邏輯和業(yè)務(wù)邏輯,分離這些成分的單元測試成了一個艱巨的任務(wù)。大多數(shù)人選擇忽略這個任務(wù),那就是不做任何測試。
上文提到了控制器可以管理視圖的層次結(jié)構(gòu);控制器有一個 “view” 屬性,并且可以通過 IBOutlet 訪問視圖的任何子視圖。當(dāng)有很多 outlet 時這樣做不易于擴(kuò)展,在某種意義上,最好不要使用子視圖控制器(child view controller)來幫助管理子視圖。在這里有多個模糊的標(biāo)準(zhǔn),似乎沒有人能完全達(dá)成一致。貌似無論如何,view 和對應(yīng)的 controller 都緊緊的耦合在一起,總之,還是會把它們當(dāng)成一個組件來對待。Apple 提供的這個組件一度以來在某種程度誤導(dǎo)了大多初學(xué)者,初學(xué)者將所有的視圖全部拖到 xib 中,連接大量的 IBoutLet 輸出口屬性,都是一些列問題。
二、MVP
從字面意思來理解,MVP即Model View Presenter(模型 視圖 協(xié)調(diào)器),MVP實現(xiàn)了Cocoa的MVC的愿景。MVP的協(xié)調(diào)器Presenter并沒有對ViewController的聲明周期做任何改變,因此View可以很容易的被模擬出來。在Presenter中根本沒有和布局有關(guān)的代碼,但是它卻負(fù)責(zé)更新View的數(shù)據(jù)和狀態(tài)。
MVP 是第一個如何協(xié)調(diào)整合三個實際上分離的層次的架構(gòu)模式,既然我們不希望 View 涉及到 Model,那么在顯示的 View Controller(其實就是 View)中處理這種協(xié)調(diào)的邏輯就是不正確的,因此我們需要在其他地方來做這些事情。例如,我們可以做基于整個 App 范圍內(nèi)的路由服務(wù),由它來負(fù)責(zé)執(zhí)行協(xié)調(diào)任務(wù),以及 View 到 View 的展示。這個出現(xiàn)并且必須處理的問題不僅僅是在 MVP 模式中,同時也存在于以下集中方案中。
MVC和MVP的區(qū)別就是,在MVP中M和V沒有直接通信。
1)MVP模式下的三個特性的分析:
任務(wù)均攤 -- 我們將最主要的任務(wù)劃分到 Presenter 和 Model,而 View 的功能較少;
可測試性 -- 非常好,由于一個功能簡單的 View 層,所以測試大多數(shù)業(yè)務(wù)邏輯也變得簡單;
易用性 -- 代碼量比 MVC 模式的大,但同時 MVP 的概念卻非常清晰。
2)iOS MVP 示意圖:

1.就 MVP 而言,UIViewController 的子類實際上就是 Views 并不是 Presenters。這點(diǎn)區(qū)別使得這種模式的可測試性得到了極大的提高,付出的代價是開發(fā)速度的一些降低,因為必須要做一些手動的數(shù)據(jù)和事件綁定。
2.還有一些其他形態(tài)的 MVP -- 監(jiān)控控制器的 MVP。這個變體包含了 View 和 Model 之間的直接綁定,但是 Presenter 仍然來管理來自 View 的動作事件,同時也能勝任對 View 的更新。

3)規(guī)范的MVP設(shè)計模式:
1、Model 層應(yīng)該不僅僅是創(chuàng)建一個數(shù)據(jù)對象,還應(yīng)該包含網(wǎng)絡(luò)請求,以及數(shù)據(jù) SQLite 的 CRUD 操作(比如 iOS 平臺,一般以 FMDB 框架直接操作 sql,或者用 CoreData) 。一般可以將數(shù)據(jù)對象是否需要緩存設(shè)計成一個字段 isCache,或者針對整個項目設(shè)計一個開存儲關(guān),決定整個項目是否需要數(shù)據(jù)緩存。我們常見的新聞類 App,在離線的時候看到的數(shù)據(jù),都是做了緩存處理的。比如一些金融類的 App,實時性比較高,是不做緩存的。
2、View 層比較簡單明,就是 View 的一些封裝、重用。在一款精心設(shè)計過的 App 里面,應(yīng)該有很多 View 是可以封裝重用的。比如一些自己的 TableViewCell,自己設(shè)計的 Button,一些 View(包含一些子 View,UI 精心設(shè)計過,在項目里多處出現(xiàn)的)等等。
3、Presenter 層并不涉及數(shù)據(jù)對象的網(wǎng)絡(luò)請求和 SQLite 操作,只是 Model 層和 View 層的一個橋梁。Presenter 層就不至于太臃腫,容易看懂。一些大的 App,或因為上線時間比較久了,經(jīng)歷過眾多程序員的修補(bǔ),或因前期并未做好架構(gòu),以至于打開一個類,幾千行的代碼,看著自己都暈。
4)MVP的優(yōu)勢
模型與視圖完全分離,我們可以修改視圖而不影響模型
可以更高效地使用模型,因為所以的交互都發(fā)生在一個地方——Presenter內(nèi)部
我們可以將一個Presener用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)
5)MVP的問題
由于對視圖的渲染放在了Presenter中,所以視圖和Persenter的交互會過于頻繁。
還有一點(diǎn)你需要明白,如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的 聯(lián)系過于緊密。一旦視圖需要變更,那么 Presenter也需要變更了。比如說,原本用來呈現(xiàn)Html的Presenter現(xiàn)在也需要用于呈現(xiàn)Pdf了,那么視圖很有可能也需要變更。
三、MVVM
從字面意思來理解,MVVM 即 Model View ViewModel(模型 視圖 視圖模型)。MVC 是一個用來組織代碼的權(quán)威范式,也是構(gòu)建 iOS App 的標(biāo)準(zhǔn)模式。Apple 甚至是這么說的。在 MVC 下,所有的對象被歸類為一個 model,一個 view,或一個 controller。Model 持有數(shù)據(jù),View 顯示與用戶交互的界面,而 View Controller 調(diào)解 Model 和 View 之間的交互。然而,隨著模塊的迭代我們越來越發(fā)現(xiàn) MVC 自身存在著很多不足。因此,MVVM 從其他應(yīng)用而出,在 iOS 中從此我們完全將業(yè)務(wù)邏輯加以區(qū)分并使用這套思想。在 MVVM 中他的設(shè)計思路和 MVC 很像。它正式規(guī)范了視圖和控制器緊耦合的性質(zhì),并引入新的組件 ViewModel。此外,它還有像監(jiān)管版本的 MVP 那樣的綁定功能,但這個綁定不是在 View 和 Model 之間而是在 View 和 ViewModel 之間。
1)MVVM 模式下的三個特性的分析:
任務(wù)均攤 -- MVVM 的 View 要比 MVP 中的 View 承擔(dān)的責(zé)任多。因為前者通過 ViewModel 的設(shè)置綁定來更新狀態(tài),而后者只監(jiān)聽 Presenter 的事件但并不會對自己有什么更新。
可測試性 -- ViewModel 不知道關(guān)于 View 的任何事情,這允許我們可以輕易的測試 ViewModel。同時 View 也可以被測試,但是由于屬于 UIKit 的范疇,對他們的測試通常會被忽略。
易用性 -- 在實際開發(fā)中必須把 View 中的事件指向 Presenter 并且手動的來更新 View,如果使用綁定的話,MVVM 代碼量將會小的多。
2)iOS MVVM示意圖:

1.在 MVVM 里,view 和 view controller 正式聯(lián)系在一起,我們把它們視為一個組件。視圖 view 仍然不能直接引用模型 Model,當(dāng)然 controller 也不能。相反,他們引用視圖模型 View Model。
2.View Model 是一個放置用戶輸入驗證邏輯,視圖顯示邏輯,發(fā)起網(wǎng)絡(luò)請求和其他各種各樣的代碼的極好的地方。有一件事情不應(yīng)歸入 View Model,那就是任何視圖本身的引用。View Model 的概念同時適用于于 iOS 和 OS X(換句話說,不要在 View Model 中使用 #import UIKit.h)。
3.由于展示邏輯(presentation logic)放在了 View Model 中(比如 Model 的值映射到一個格式化的字符串),視圖控制器本身就會不再臃腫。當(dāng)然你開始使用 MVVM 的最好方式時可以先將一小部分邏輯放入視圖模型,然后當(dāng)你逐漸習(xí)慣于使用這個范式的時候再遷移更多的邏輯到視圖模型中。
使用 MVVM 會輕微的增加代碼量,但總體上減少了代碼的復(fù)雜性。
文章參考?http://www.itdecent.cn/p/f8806c2f3ee3