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

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

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

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

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