1. 程式人生 > >iOS開發之利用MVVM框架來優化專案結構。對Controller瘦身以及MVC向MVVM框架的遷移。

iOS開發之利用MVVM框架來優化專案結構。對Controller瘦身以及MVC向MVVM框架的遷移。

MVC開發模式 : 
1. 蘋果官方一直推薦我們開發者使用MVC的開發模式,所以我們大部分人之前的專案都是用MVC來開發APP,這樣開發,肯定會發現一個超級大的弊端,viewcontroller裡邊有大量的業務邏輯與檢視操作邏輯,隨著專案的不斷的迭代,會充斥著大量的問題,我們的單元測試也好, 我們的邏輯設計,以及程式碼的整潔性,程式碼的層級性都會出現很多的問題,為此我覺得為Controller瘦身已經是非常必要的。  也有一部分開發者著不同意這種思想,但是我們也不必固守成規,雖然MVVM想必與MVC可能從程式碼上來講並沒有減少,反而可能增加。 但是相對應的,也會帶來很多好處,下面就是MVVM 使用後的一些優點。

MVVM的優點如下:

MVVM模式和MVC模式一樣,主要目的是分離檢視(View)和模型(Model),有以下四大優點
1. 低耦合。檢視(View)可以獨立於Model變化和修改,一個ViewModel可以繫結到不同的"View"上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。
2. 可重用性。你可以把一些檢視邏輯放在一個ViewModel裡面,讓很多view重用這段檢視邏輯。
3. 獨立開發。開發人員可以專注於業務邏輯和資料的開發(ViewModel),設計人員可以專注於頁面設計,使用Expression Blend可以很容易設計介面並生成xaml程式碼。
4. 可測試
。介面素來是比較難於測試的,而現在測試可以針對ViewModel來寫。

下面通過程式碼來進行來進行說明

1.首先專案的層級關係圖。


1.model資料夾: 這個和mvc下的model沒有什麼區別。 一般為瘦model. 

2. view資料夾:  檢視的載體, 和mvc下的view也沒有什麼區別, 由controller進行協調展示。

3. Controller資料夾 :  只包含一些檢視的操作,不包含任何的業務處理。這裡要達到瘦身的效果,經常會吧tableview 的protocal給單獨提出來,本文章將的也會把這些類給提出來,後面詳細說明

4.ViewModel:  把以前充斥在Controller中的一些業務處理放置在此地, 包含:資料請求,資料的包裝。把封裝好的資料直接傳遞給Controller直接進行顯示。 網路請求的起飛與著陸點,這個看專案的網路層架構設計。 不能一視同仁

5.TableViewProtocol資料夾:  這個就是把Tableview的資料來源與delegate給提取出來,這樣做的好處就是可以最大化的重用對應的代理方法,易於管理, 並且可以讓controller中及其的簡潔。

6.3rdLibs資料夾: 存放了一個第三方的上下拉載入控制元件。

2.完整的專案程式碼結構目錄:


下面讓我們一級一級的進行程式碼的解剖:

1.Controller程式碼解剖:

TableViewController.h

#import <UIKit/UIKit.h>
@interface TableViewController : UIViewController

@end

TableViewController.m

#import "TableViewController.h"
#import "YiRefreshHeader.h"
#import "YiRefreshFooter.h"
#import "TableViewModel.h"
#import "TableViewDataSource.h"
#import "TableViewDelegate.h"
@interface TableViewController ()
{
    
    YiRefreshHeader *refreshHeader;
    YiRefreshFooter *refreshFooter;
    NSMutableArray *totalSource;
    TableViewModel *tableViewModel;
    UITableView *tableView;
    TableViewDataSource *tableViewDataSource;
    TableViewDelegate *tableViewDelegate;
}

@end

@implementation TableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    if (iOS7) {
        self.edgesForExtendedLayout = UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight;
    }
    [email protected]"MVVMDemo With TableView";
    self.view.backgroundColor=[UIColor whiteColor];
    
    tableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 0, WScreen, HScreen-64) style:UITableViewStylePlain];
    [self.view addSubview:tableView];
    tableViewDataSource = [[TableViewDataSource alloc] init]; //對tableview 資料來源的封裝的類
    tableViewDelegate = [[TableViewDelegate alloc] init];     //對tableview 代理 封裝的類,可以方便複用
    tableView.dataSource=tableViewDataSource;                 //把當前的tableview的資料來源指向我們的公用datasource類。
    tableView.delegate=tableViewDelegate;                     //把當前的tableview的代理指向我們的公用的代理類。
    tableViewModel=[[TableViewModel alloc] init];             //本章的重頭戲,把業務處理提出的類
    totalSource=0;
    
    //    YiRefreshHeader  頭部重新整理按鈕的使用
    refreshHeader=[[YiRefreshHeader alloc] init];
    refreshHeader.scrollView=tableView;
    [refreshHeader header];
    __weak typeof(self) weakSelf = self;
    refreshHeader.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf headerRefreshAction];                     //下拉重新整理
    };
    
    //    是否在進入該介面的時候就開始進入重新整理狀態
    [refreshHeader beginRefreshing];
    
    //    YiRefreshFooter  底部重新整理按鈕的使用
    refreshFooter=[[YiRefreshFooter alloc] init];
    refreshFooter.scrollView=tableView;
    [refreshFooter footer];
    
    refreshFooter.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf footerRefreshAction];                     //上拉重新整理
    };
    
}

// 這裡是具體的下拉重新整理方法,我們可以發現,這裡是呼叫了viewModel中的方法,並把處理的結果通過block方式返回回來。
- (void)headerRefreshAction
{
   
    [tableViewModel headerRefreshRequestWithCallback:^(NSArray *array){
        totalSource=(NSMutableArray *)array;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshHeader endRefreshing];
        [tableView reloadData];
    }];

}

// 這裡是具體的上拉重新整理方法,我們可以發現,這裡是呼叫了viewModel中的方法,並把處理的結果通過block方式返回回來。
- (void)footerRefreshAction
{
    [tableViewModel footerRefreshRequestWithCallback:^(NSArray *array){
        [totalSource addObjectsFromArray:array] ;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshFooter endRefreshing];
        [tableView reloadData];
    
    }];
  
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end


TableViewModel.h

#import <Foundation/Foundation.h>

typedef void (^callback) (NSArray *array);

@interface TableViewModel : NSObject

//tableView頭部重新整理的網路請求
- (void)headerRefreshRequestWithCallback:(callback)callback;
//tableView底部重新整理的網路請求
- (void)footerRefreshRequestWithCallback:(callback)callback;

@end

TableViewModel.m ,    這裡應該非常的通俗易懂,就是把以前ViewController中的上下拉重新整理方法放在此地,並且通過block把處理過後的結果返回給控制器。
#import "TableViewModel.h"
#import "CustomModel.h"
@interface TableViewModel ()

@end

@implementation TableViewModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

- (void)headerRefreshRequestWithCallback:(callback)callback
{
        //  後臺執行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主執行緒重新整理檢視
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"下拉重新整理-測試資料%d",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

- (void )footerRefreshRequestWithCallback:(callback)callback
{
        //  後臺執行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主執行緒重新整理檢視
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"上拉載入 -測試資料%d",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

@end

TableViewDataSource.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TableViewDataSource : NSObject<UITableViewDataSource>

@property (nonatomic,strong) NSArray *array;

@end
TableViewDataSource.m   把tableview的 資料來源相關的代理方法全部擰出來,以後可以進行更高的複用
#import "TableViewDataSource.h"
#import "CustomTableViewCell.h"

@implementation TableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _array.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.titleLabel.text=((CustomModel *)[_array objectAtIndex:indexPath.row]).title;
    return cell;
}

@end

TableViewDelegate.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface TableViewDelegate : NSObject<UITableViewDelegate>
@property (nonatomic,strong) NSArray *array;
@end

TableViewDelegate.m

這裡我就把經常使用的tableview cell的點選代理給寫了出來,大家可以隨意進行新增其他的代理。 

#import "TableViewDelegate.h"

@implementation TableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (_array.count>0) {
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:((CustomModel *)[_array objectAtIndex:indexPath.row]).title delegate:nil cancelButtonTitle:@"sure" otherButtonTitles:nil, nil];
        [alert show];
    }    
}
@end

其中。 model, 已經自定義的cell的具體程式碼我在這兒就不一一展示,和我們以前MVC的寫法一模一樣。  

專案程式碼下載地址: 點選開啟連結

總結:

 1. 利用ViewModel把業務處理操作,全部從Controller中提出,這樣在Controller中就不會充斥著大量的網路代理回撥

2. 利用封裝tableview的資料來源 與 代理 類 ,來優化大量tableview的 協議 的冗餘程式碼。減少程式碼量,方便後期維護與擴充套件。

後續:

      既然我們把業務操作提取出來了。那如何展示出來,怎麼把對應的資料傳遞到view層等等,這時候就誕生了ReactiveCocoa, 雖然前期有自學過,但一直沒有理解到其精髓,只是會使用一些基本的用法,運用到專案中間來,我怕會有一些意想不到的坑, 所以不敢拿公司的利益來冒險,以後有時間。 自己寫寫demo來運用下就好。 後期再出對應的部落格詳細講解它

相關推薦

iOS開發利用MVVM框架優化專案結構Controller以及MVCMVVM框架遷移

MVC開發模式 : 1. 蘋果官方一直推薦我們開發者使用MVC的開發模式,所以我們大部分人之前的專案都是用MVC來開發APP,這樣開發,肯定會發現一個超級大的弊端,viewcontroller裡邊有大量的業務邏輯與檢視操作邏輯,隨著專案的不斷的迭代,會充斥著大量的問題,我們

iOS開發自定義導航欄返回按鈕右滑返回手勢失效的解決---親測是有效的

問題一:怎麼自定義leftItem問題二:為什麼系統自帶的右滑返回手勢失效問題三:怎麼解決這個失效問題3.怎麼解決這個失效問題 其實很簡單很簡單~只需要新增下面這一句程式碼即可self.navigationController.interactivePopGestureRe

iOS開發CFNetwork框架使用

iOS開發之CFNetwork框架使用 一、引言     在iOS應用開發中,CFNetwork框架其實並不是非常常用的,相對NSURLSession框架而言,這是一個相對底層的網路工作框架。官方文件中的下圖描述了CFNetwork在整個網路體系中的位置:

iOS開發AVKit框架使用

iOS開發之AVKit框架使用 一、引言     在iOS開發框架中,AVKit是一個非常上層,偏應用的框架,它是基於AVFoundation的一層檢視層封裝。其中相關檔案和類都十分簡單,本篇部落格主要整理和總結AVKit中相關類的使用方法。 二、A

iOS開發AddressBookUI框架詳解

iOS開發之AddressBookUI框架詳解 一、關於AddressBookUI框架     AddressbookUI是iOS開發框架中提供的一套通訊錄介面元件。其中封裝好了一套選擇聯絡人,檢視聯絡人的介面,在需要時開發者可以直接呼叫。當然對於聯絡人介面,

iOS開發AddressBook框架詳解

iOS開發之AddressBook框架詳解 一、寫在前面     首先,AddressBook框架是一個已經過時的框架,iOS9之後官方提供了Contacts框架來進行使用者通訊錄相關操作。儘管如此,AddressBook框架依然是一個非常優雅並且使用方便的通

iOS開發Accounts框架詳解

iOS開發之Accounts框架詳解     Accounts框架是iOS原生提供的一套賬戶管理框架,其支援Facebook,新浪微博,騰訊微博,Twitter和領英賬戶管理的功能。需要注意,在iOS 11及以上系統中,將此功能已經刪除,因此Accounts.frame

iOS開發AssetsLibrary框架使用

iOS開發之AssetsLibrary框架使用 一、引言     AssetsLibrary框架是專門用來操作相簿相關資源的一個框架,其是iOS4到iOS9之間常使用的一個框架,在iOS9之後,系統系統了Photos框架代替了AssetsLibrary框架,但

iOS開發AdSupport框架使用

iOS開發之AdSupport框架使用     AdSupport從字面意思上理解是用來進行廣告支援,這個框架十分簡單,裡面只有一個類,類中只有一個方法和兩個屬性。     AdSupport的唯一用途是用來獲取裝置唯一的一個廣告識別符號。可以使

iOS開發BusinessChat框架使用

iOS開發之BusinessChat框架使用       BusinessChat是iOS11.3後引入的新框架,這個框架配合iMessage應用將商家與使用者更加緊密的結合起來,並且為商家提供了另外一種非常方便的客服系統。     &

iOS開發優化tableView卡頓現象

1.複用單元格; 2.使用不透明的試圖,單元格中儘量少使用動畫; 3.圖片使用非同步載入同時設定圖片載入的併發數; 4.滑動時不載入圖片,滑動結束開始載入; 5.文字圖片可以直接drawInRect繪製; 6.非必要條件下,減少重新整理的cell; 7.如果ce

onvif開發利用gSOAP生成onvif客戶端程式碼框架

cd gsoap-2.8 ./configure make sudo make install 命令列模式下敲入命令:wsdl2h -V 檢視gSOAP軟體版本,有版本出現則安裝成功。 2. 利用gSOAP生成onvif客戶端程式碼框架 2.1 在當前目錄下

ios開發使用bundle管理資原始檔

轉載自:http://blog.csdn.net/kylinbl/article/details/9047209 (chenyong 將資原始檔夾弄成bundle格式的特簡單單,只需要把字尾名改成.bundle即可)  在ios開發中為了方便管理資原始檔,可以使用bundl

iOS開發ReactiveCocoa下的MVVM

最近工作比較忙,但還是出來更新部落格了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,幹活還是比較足的。在之前發表過一篇博文,名字叫做,大體上講的就是使用Block回撥的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函式響應式程式設計的框架,並且有許多人用它來更

iOS開發Masonry框架原始碼解析

Masonry是iOS在控制元件佈局中經常使用的一個輕量級框架,Masonry讓NSLayoutConstraint使用起來更為簡潔。Masonry簡化了NSLayoutConstraint的使用方式,讓我們可以以鏈式的方式為我們的控制元件指定約束。本篇部落格的主題不是教你如何去使用Masonry框架的,而是

iOS開發淺談MVVM的架構設計與團隊協作

1 // 2 // NetRequestClass.m 3 // MVVMTest 4 // 5 // Created by 李澤魯 on 15/1/6. 6 // Copyright (c) 2015年 李澤魯. All rights reserved. 7

iOS開發ReactiveCocoa下的MVVM(乾貨分享)

最近工作比較忙,但還是出來更新部落格了,今天給大家分享一些ReactiveCocoa以及MVVM的一些東西,幹活還是比較足的。在之前發表過一篇博文,名字叫做,大體上講的就是使用Block回撥的方式實現MVVM的。在寫上篇文章時也知道有ReactiveCocoa這個函式響應式程式

iOS開發Masonry框架-原始碼解析

Masonry是iOS在控制元件佈局中經常使用的一個輕量級框架。Masonry讓NSLayoutConstraint使用起來更為簡潔。Masonry簡化了NSLayoutConstraint的使用方式,讓我們可以以鏈式的方式為我們的控制元件指定約束。本篇是對Masonry框架的原始碼進行解析,讓你明白Maso

iOS開發CoreSpotlight框架的應用

iOS開發之CoreSpotlight框架的應用    

iOS開發DeviceCheck框架的應用

iOS開發之DeviceCheck框架的應用       DeviceCheck框架是iOS 1