為什么要用MVVM替代MVC
在MVC模式中交煞,Controller由于承擔(dān)了過多的事務(wù)括堤,包括頁面展示邏輯和業(yè)務(wù)邏輯搬葬,往往會變的臃腫不堪荷腊,成為一個人們所說的重量級視圖控制器。臃腫的ViewController難以理解急凰,難以維護女仰,難以擴展,增加了后續(xù)開發(fā)的復(fù)雜度抡锈,降低了整體開發(fā)的效率疾忍。
什么是MVVM
MVC
MVC(Model-View-Controller)是最經(jīng)典的架構(gòu)模式,其中Model是作為數(shù)據(jù)管理者企孩,View作為數(shù)據(jù)展示者锭碳,Controller作為數(shù)據(jù)加工者,Model和View又都是由Controller來根據(jù)業(yè)務(wù)需求調(diào)配勿璃,所以Controller還負擔(dān)了一個數(shù)據(jù)流調(diào)配的功能擒抛。
MVCS
MVCS是基于MVC衍生出來的一套架構(gòu)推汽。從概念上來說,它拆分的部分是Model部分歧沪,拆出來一個Store歹撒。這個Store專門負責(zé)數(shù)據(jù)存取。
MVVM
MVVM本質(zhì)上也是從MVC中派生出來的诊胞,它是一個精心優(yōu)化的MVC架構(gòu)暖夭。它把數(shù)據(jù)加工的任務(wù)從Controller中解放了出來,使得Controller只需要專注于數(shù)據(jù)調(diào)配的工作撵孤,ViewModel則去負責(zé)數(shù)據(jù)加工并通過通知機制讓View響應(yīng)ViewModel的改變迈着。大部分MVVM架構(gòu)都會使用ReactiveCocoa,ReactiveCocoa帶來了信號通知效果邪码。
![](http://ubuntu.myweimai.com/content/uploadfile/201608/c48e1470713299.png)
當然MVVM也有缺點裕菠,數(shù)據(jù)綁定機制讓調(diào)試更困難,界面上出現(xiàn)的bug闭专,可能是view的代碼有問題奴潘,也可能是model有問題。數(shù)據(jù)綁定機制讓一個位置的bug快速傳遞到其它位置上影钉,定位原始出問題的地方不那么容易画髓。而且數(shù)據(jù)綁定需要花費更大的內(nèi)存。MVVM的學(xué)習(xí)和開發(fā)成本也很高平委,大家對它不熟悉奈虾,基于綁定機制進行編程需要一定時間的學(xué)習(xí)才能上手。
MVVM簡化版
MVVM簡化版是在MVC和MVVM兩種架構(gòu)中權(quán)衡而產(chǎn)生的架構(gòu)肆汹。它引用了ViewModel愚墓,將表示邏輯移到ViewModel中。一個view對應(yīng)一個ViewModel昂勉,其包含了這個View數(shù)據(jù)展示和樣式定制所需要的所有數(shù)據(jù)浪册。同時,不再引用雙向數(shù)據(jù)綁定機制岗照,而使用傳統(tǒng)的代理回調(diào)將ui事件傳遞給外界村象。這個架構(gòu)中,ViewModel負責(zé)處理數(shù)據(jù)展示和樣式定制的邏輯攒至; ViewController僅僅負責(zé)數(shù)據(jù)流調(diào)配厚者。
MVVM簡化版有以下三個優(yōu)點:
- 簡單,易學(xué)易用迫吐,上手成本低
- 兼容MVC模式
- 提高應(yīng)用的可測試性
怎么使用MVVM
下面我們用MVVM簡化版寫一個簡單的demo库菲,看看它是如何工作的。
這是一個獲取最新天氣的demo志膀,為簡單起見熙宇,只顯示今天的天氣鳖擒。從服務(wù)返回的json格式如下:
......
"today": {
"temperature": "28℃~38℃",
"date_y": "2016年08月19日",
"weather_id": {
"fa": "00",
"fb": "00"
},
"city": "杭州",
......
}
要求在頁面上按MMDD格式顯示日期,天氣情況不能用00烫止、01直接顯示蒋荚,要轉(zhuǎn)換一下。這些都屬于展示邏輯馆蠕。我們將它從ViewController中移到ViewModel中期升。
首先建立一個Weather model,它僅僅定義了幾個屬性來表達數(shù)據(jù)互躬,沒有任何數(shù)據(jù)加工的部分播赁。
struct Weather {
var location: String? //位置
var date: String? //日期
var iconText: String? //天氣圖標
var temperature: String? //溫度
}
WeatherViewModel有自己的屬性,提供給對應(yīng)的View吼渡。它傳入weather model行拢,加工數(shù)據(jù)以滿足view需要。
class WeatherViewModel: NSObject {
var date: String?
var iconText: String?
var loction: String?
var temperature: String?
......
展示邏輯的處理如下:
class func viewModelWith(weather: Weather) -> WeatherViewModel{
let date = weather.date?.toDate(DateFormat.Custom("yyyy年MM月dd日"))
var iconText = ""
switch weather.iconText! {
case "00":
iconText = "晴"
case "01":
iconText = "多云"
case "02":
iconText = "陰"
case "03":
iconText = "陣雨"
case "04":
iconText = "雷陣雨"
case "07":
iconText = "小雨"
case "08":
iconText = "中雨"
case "09":
iconText = "大雨"
case "10":
iconText = "暴雨"
case "11":
iconText = "特大暴雨"
case "21":
iconText = "小雨-中雨"
case "22":
iconText = "中雨-大雨"
case "23":
iconText = "大雨-暴雨"
default: break
}
let wvm = WeatherViewModel()
wvm.date = String(date!.month) + "\\" + String(date!.day)
wvm.loction = weather.location
wvm.temperature = weather.temperature
wvm.iconText = iconText
return wvm
}
View只需要定義好裝配ViewModel的接口和定義好UI回調(diào)事件即可诞吱。
class WeatherView: UIView {
var timeLbl: UILabel!
var iconLbl: UILabel!
var temperationLbl: UILabel!
var cityLbl: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func bindDataWithViewModel(weatherViewModel: WeatherViewModel){
timeLbl.text = weatherViewModel.date
iconLbl.text = weatherViewModel.iconText
temperationLbl.text = weatherViewModel.temperature
cityLbl.text = weatherViewModel.loction
}
......
ViewController管理View Controller的生命周期,負責(zé)生成所有的View實例竭缝,并放入到View Controller中房维。監(jiān)聽處理來自View和業(yè)務(wù)相關(guān)的事件。
......
let weather = Weather(location: location, date: date, iconText: icon, temperature: temperature)
let wv = WeatherViewModel.viewModelWith(weather)
self.todayView.bindDataWithViewModel(wv)
......
demo的源碼在這里:[https://github.com/superzcj]
總結(jié)
MVVM將展示和業(yè)務(wù)邏輯從Controller中移到ViewModel抬纸,為Controller減輕了負擔(dān)咙俩,使代碼結(jié)構(gòu)更清晰,職責(zé)更明確湿故,同時使代碼更易于測試阿趁。
簡化的MVVM也能跟現(xiàn)有的MVC應(yīng)用兼容,不會對MVC模式帶來太多變化坛猪,只是把部分邏輯代碼移動個位置脖阵。另外對于簡單的業(yè)務(wù)功能,使用MVVM會頻繁在各個部分傳遞數(shù)據(jù)墅茉,反而顯得臃腫羅嗦命黔。因此,對于簡單的業(yè)務(wù)就斤,我們?nèi)钥梢杂肕VC模式開發(fā)悍募,對于復(fù)雜的業(yè)務(wù),我們使用MVVM模式拆分業(yè)務(wù)邏輯洋机。