iOS MVVM 實踐

為什么要用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帶來了信號通知效果邪码。



當然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ù)邏輯洋机。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坠宴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绷旗,更是在濱河造成了極大的恐慌喜鼓,老刑警劉巖副砍,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異颠通,居然都是意外死亡址晕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門顿锰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谨垃,“玉大人,你說我怎么就攤上這事硼控×跆眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵牢撼,是天一觀的道長匙隔。 經(jīng)常有香客問我,道長熏版,這世上最難降的妖魔是什么纷责? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮撼短,結(jié)果婚禮上再膳,老公的妹妹穿的比我還像新娘。我一直安慰自己曲横,他們只是感情好喂柒,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禾嫉,像睡著了一般灾杰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上熙参,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天艳吠,我揣著相機與錄音,去河邊找鬼孽椰。 笑死讲竿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的弄屡。 我是一名探鬼主播题禀,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膀捷!你這毒婦竟也來了迈嘹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秀仲,沒想到半個月后融痛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡神僵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年雁刷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片保礼。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡沛励,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炮障,到底是詐尸還是另有隱情目派,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布胁赢,位于F島的核電站企蹭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏智末。R本人自食惡果不足惜谅摄,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望系馆。 院中可真熱鬧螟凭,春花似錦、人聲如沸它呀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纵穿。三九已至,卻和暖如春奢人,著一層夾襖步出監(jiān)牢的瞬間谓媒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工何乎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留句惯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓支救,卻偏偏與公主長得像抢野,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子各墨,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容