你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 總結整理下一個快速開發MVVM框架

總結整理下一個快速開發MVVM框架

編輯:IOS開發基礎

65.png

做iOS開發也有一段時間了,最近閒暇之余學習研究了下MVVM,每個人對架構和設計模式都有不同的理解,在此記錄下我對MVVM的一些小見解,僅供參考,歡迎批評指正。

概述

引用自iOS應用架構談

MVVM的出現主要是為了解決在開發過程中Controller越來越龐大的問題,變得難以維護,所以MVVM把數據加工的任務從Controller中解放了出來,使得Controller只需要專注於數據調配的工作,ViewModel則去負責數據加工並通過通知機制讓View響應ViewModel的改變。

MVVM是基於胖Model的架構思路建立的,然後在胖Model中拆出兩部分:Model和ViewModel。ViewModel本質上算是Model層(因為是胖Model裡面分出來的一部分),所以View並不適合直接持有ViewModel,因為ViewModel有可能並不是只服務於特定的一個View,使用更加松散的綁定關系能夠降低ViewModel和View之間的耦合度。

還有一個讓人很容易忽略的問題,大部分國內外資料闡述MVVM的時候都是這樣排布的:

ViewViewModel Model

造成了MVVM不需要Controller的錯覺,現在似乎發展成業界開始出現MVVM是不需要Controller的聲音了。其實MVVM是一定需要Controller的參與的,雖然MVVM在一定程度上弱化了Controller的存在感,並且給Controller做了減負瘦身(這也是MVVM的主要目的)。但是,這並不代表MVVM中不需要Controller,MMVC和MVVM他們之間的關系應該是這樣:

View  C  ViewModel Model

所以使用MVVM之後,就不需要Controller的說法是不正確的。嚴格來說MVVM其實是MVCVM。從中可以得知,Controller夾在View和ViewModel之間做的其中一個主要事情就是將View和ViewModel進行綁定。在邏輯上,Controller知道應當展示哪個View,Controller也知道應當使用哪個ViewModel,然而View和ViewModel它們之間是互相不知道的,所以Controller就負責控制他們的綁定關系,所以叫Controller/控制器就是這個原因。

前面扯了那麼多,其實歸根結底就是一句話:在MVC的基礎上,把C拆出一個ViewModel專門負責數據處理的事情,就是MVVM。然後,為了讓View和ViewModel之間能夠有比較松散的綁定關系,於是我們使用ReactiveCocoa,KVO,Notification,block,delegate和target-action都可以用來做數據通信,從而來實現綁定,但都不如ReactiveCocoa提供的RACSignal來的優雅,如果不用ReactiveCocoa,綁定關系可能就做不到那麼松散那麼好,但並不影響它還是MVVM。

MVVM(View-ViewManger-C-ViewModel-Model)

QQ截圖20160229104239.png

  • View - 用來呈現用戶界面

  • ViewManger - 用來處理View的常規事件,負責管理View

  • Controller - 負責ViewManger和ViewModel之間的綁定,負責控制器本身的生命周期。

  • ViewModel - 存放各種業務邏輯和網絡請求

  • Model - 用來呈現數據 

QQ截圖20160229104310.png

這種設計的目的是保持View和Model的高度純潔,提高可擴展性和復用度。在日常開發中,ViewModel是為了拆分Controller業務邏輯而存在的,所以ViewModel需要提供公共的服務接口,以便為Controller提供數據。而ViewManger的作用相當於一個小管家,幫助Controller來分別管理每個subView,ViewManger負責接管來自View的事件,也負責接收來自Controller的模型數據,而View進行自己所負責的視圖數據綁定工作。Controller則是最後的大家長,負責將ViewModel和ViewManger進行綁定,進行數據轉發工作。把合適的數據模型分發給合適的視圖管理者。

??日常開發中,往往一個視圖頁面交由一個控制器進行管理,而一個頁面上又有N個小的子頁面,這就要求我們來對這些視圖進行合適的分層處理,拆分視圖,將這些視圖進行封裝,將復雜View抽象成獨立的類,不必暴露出具體的實現細節。這樣做的好處是,簡化應用層的層級復雜度,同時也方便進行管理,視圖結構就會變得很清晰。子視圖具體的內部事件,可通過代理模式或者Block交由ViewManger處理,因為視圖是可以復用的,而其中的事件響應代碼往往是根據不同的業務是有差異的。所以可能會有下面兩種情況出現:

  • View很純潔,需要復用View,若業務邏輯變化則切換ViewManger。

  • ViewManger也比較純潔,若業務邏輯不變,而View需要大變,則切換View即可,保證View中的protocol或者block一致即可<最好是通過協議提前規范好>。

這樣就實現了互相的封裝,兩者之間只通過protocol或者block進行交流通信,降低了代碼的耦合度。盡量使用protocol和category來制定對象之間的通信規范,來降低代碼的侵入性。

這樣的架構設計,就像一條生產線,ViewModel進行數據的采集和加工,Controller則進行數據的裝配和轉發工作,ViewManger進行接收轉發分配來的數據,從而進行負責View的展示工作和管理View的事件。這樣,不管哪個環節,都是可以更換的,同時也提高了復用性。

架構講解

screenshot.png

以上圖做為講解demo,最然很簡單,但是也能夠很好的闡述了,理解思想才是最重要的。 首先我們來拆分這個頁面,第一個為控制器。暫且命名為MyController,上面有兩個直接子視圖,按鈕MyBtn和頁面比較復雜的子視圖MyView,MyView中有MyViewBtn1和MyViewBtn2還有一個MyViewLabel視圖。 具體結構如下:

QQ截圖20160229104443.png

??界面分析完了,現在可以進行代碼的架構工作了。 首先需要建立一個ViewModel,使它能夠源源不斷的進行數據的生產,並提供數據給MyController;然後建立一個ViewManger負責管理MyView,當然,Model模型數據必不可少。這些工作完成之後,代碼結構變為:

QQ截圖20160229104452.png

控制器中的代碼結構如下圖:

QQ截圖20160229104536.png

當用戶點擊MyBtn按鈕觸發動作時,控制器就會就將ViewMode中加載的數據模型轉發分配給ViewManger中的sui_model屬性接收。

- (IBAction)clickBtnAction:(UIButton *)sender {
   self.thirdViewManger.sui_model = [self.viewModel getRandomData];
}

其中,MyViewModel中的加載代碼如下,如上所述,它的工作就是分解以前控制器做的一些事情。

- (void)vm_getDataSuccessHandler:(void (^)())successHandler {
   // 博客中省略,查看詳細請參考demo
}

- (instancetype)getRandomData {
    if (self.dataArrayList.count > 0) {
        u_int32_t index = arc4random_uniform((u_int32_t)self.dataArrayList.count);
        return self.dataArrayList[index];
    }
    return nil;
}

MyViewManger中的代碼如下,它實現了MVVMViewMangerProtocol協議的三個方法:

// 此方法用來接收處理來自所管理View的一些事件。
- (void)handleViewMangerWithSubView:(UIView *)subView
// 此方法將view的父視圖傳遞過來,用來布局當前View
- (void)handleViewMangerWithSuperView:(UIView *)superView
// 根據所傳入的view和info信息分別實現具體的方法
- (void)handleViewMangerActionWithView:(UIView *)view info:(NSString *)info
- (void)handleViewMangerWithSubView:(UIView *)subView {
    __weak typeof(self.thirdView) weakThirdView =  self.thirdView;
    __weak typeof(self) weakSelf = self;
    
    // btnClickBlock
    weakThirdView.btnClickBlock = ^() {
        [weakSelf handleViewMangerActionWithView:weakThirdView info:@"click"];
    };
    
    // btnJumpBlock
    weakThirdView.btnJumpBlock = ^() {
        [weakSelf handleViewMangerActionWithView:weakThirdView info:@"jump"];
    };
}

- (void)handleViewMangerWithSuperView:(UIView *)superView {
    self.thirdView.frame = CGRectMake(0, 66, [UIScreen mainScreen].bounds.size.width, 200);
    [superView addSubview:self.thirdView];
}

- (void)handleViewMangerActionWithView:(UIView *)view info:(NSString *)info {
    if ([info isEqualToString:@"click"]) {
        [view configureViewWithCustomObj:self.sui_model];
    } else {
        FirstVC *firstVC = [UIViewController svv_viewControllerWithStoryBoardName:@"Main" identifier:@"FirstVCID"];
        [view.sui_currentVC.navigationController pushViewController:firstVC animated:YES];
    }
}

MyView中的代碼如下,主要是負責管理自身的內部控件視圖,並根據業務邏輯需要定義了一些基本事件,通過交給ViewManger來實現:

- (IBAction)testBtnClick:(UIButton *)sender {
    if (self.btnClickBlock) {
        self.btnClickBlock();
    }
}

- (IBAction)jumpOtherVC:(UIButton *)sender {
    if (self.btnJumpBlock) {
        self.btnJumpBlock();
    }
}

// 根據傳入的model配置需要顯示的內容
- (void)configureViewWithCustomObj:(id)obj {
    if (!obj) return;
    ThirdModel *thirdModel = (ThirdModel *)obj;
    self.testLabel.text = thirdModel.title;
}

這樣把各個部分區分開來,是不是感覺代碼結構十分清晰了呢,當然可以根據個人習慣來進行修改,代碼實現因人而異,但是思想確是互通的。把合適的業務邏輯交給最合適的對象去處理實現,只需要遵守這麼一個基本原則就可以了。

至於是否采用更輕量級的ViewController做法,即 通過將各個 protocol 的實現挪到 ViewController 之外,來為 ViewController 瘦身 ,眾口不一。以UITableView為例,我的做法是:

  • 如果只是在頁面上進行簡單的展示,並不設計負責的業務邏輯時,會將UITableViewDelegate與UITableViewDataSource單獨放到一個Handler鐘進行處理,抽象出tableHander,由MVVMTableDataDelegate進行負責管理;

  • 當然,事實上,實際開發中,每個tableView頁面都很復雜,有很多邏輯要處理,這時候只能考慮將protocol重新請回Controller中了,因為View層與ViewController層本身是持有與被持有的依賴關系,所以任何類作為ViewController的類內實例來實現協議回調,實際上都是在跨層調用,所以,隨著時間和業務邏輯的愈來愈復雜,就注定要以額外的接口為代價,換言之,ViewController 的內聚性變差了。

總之,具體情況具體分析,采用最合適的方式來處理應對不同的問題。兵來將擋,水來土掩。本文的相關Demo見github(https://github.com/lovemo/MVVMFramework),實現的功能並不復雜,僅供參考,歡迎補充。

如果想學習更多關於MVVM的文章,請參考本項目demo中下方的推薦文章。

  • 寫給iOS小白的MVVM教程(三): 網絡請求類重構實戰–基於RAC和AFN重構LeanCloud的Rest Api

  • 多方位全面解析:如何正確地寫好一個界面

  • iOS應用架構談 view層的組織和調用方案

  • 用Model-View-ViewModel構建iOS App

  • 淺談iOS中MVVM的架構設計與團隊協作

  • 一次簡單的 ViewModel 實踐

  • 不要把ViewController變成處理tableView的"垃圾桶"

  • 實踐干貨!猿題庫 iOS 客戶端架構設計

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved