iOS 開發(fā)模式

7.6 開發(fā)模式

MVC

iOS - MVC 架構(gòu)模式 - QianChia - 博客園

  • 介紹

    • Model和View永遠(yuǎn)不能相互通信,只能通過Controller傳遞。
    • Controller可以直接與Model對(duì)話,Model通過Notification和KVO機(jī)制與Controller間接通信。
    • Controller可以直接與View對(duì)話,通過outlet,直接操作View,outlet 直接對(duì)應(yīng)到View中的控件,View通過action向Controller報(bào)告事件的發(fā)生。Controller是View的直接數(shù)據(jù)源。Controller是View的代理,以同步 View與Controller。
    2c8eac8a.png
  • M:模型(Model)

    • 模型對(duì)象封裝了應(yīng)用程序的數(shù)據(jù),并定義操控和處理該數(shù)據(jù)的邏輯和運(yùn)算。例如,模型對(duì)象可能是表示商品數(shù)據(jù) list。用戶在視圖層中所進(jìn)行的創(chuàng)建或修改數(shù)據(jù)的操作,通過控制器對(duì)象傳達(dá)出去,最終會(huì)創(chuàng)建或更新模型對(duì)象。模型對(duì)象更改時(shí)(例如通過網(wǎng)絡(luò)連接接收到新數(shù)據(jù)),它通知控制器對(duì)象,控制器對(duì)象更新相應(yīng)的視圖對(duì)象。
  • V:視圖(view)

    • 視圖對(duì)象是應(yīng)用程序中用戶可以看見的對(duì)象。視圖對(duì)象知道如何將自己繪制出來,可能對(duì)用戶的操作作出響應(yīng)。視圖對(duì)象的主要目的就是顯示來自應(yīng)用程序模型對(duì)象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯。盡管如此,在 MVC 應(yīng)用程序中,視圖對(duì)象通常與模型對(duì)象分離。

    • 在iOS應(yīng)用程序開發(fā)中,所有的控件、窗口等都繼承自 UIView,對(duì)應(yīng) MVC 中的 V。UIView 及其子類主要負(fù)責(zé) UI 的實(shí)現(xiàn),而 UIView 所產(chǎn)生的事件都可以采用委托的方式,交給 UIViewController 實(shí)現(xiàn)。

  • C:控制器(controller)

    • 在應(yīng)用程序的一個(gè)或多個(gè)視圖對(duì)象和一個(gè)或多個(gè)模型對(duì)象之間,控制器對(duì)象充當(dāng)媒介??刂破鲗?duì)象因此是同步管道程序,通過它,視圖對(duì)象了解模型對(duì)象的更改,反之亦然??刂破鲗?duì)象還可以為應(yīng)用程序執(zhí)行設(shè)置和協(xié)調(diào)任務(wù),并管理其他對(duì)象的生命周期。

    • 控制器對(duì)象解釋在視圖對(duì)象中進(jìn)行的用戶操作,并將新的或更改過的數(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。

MVC 自身的不足

  • MVC 是一個(gè)用來組織代碼的權(quán)威范式,也是構(gòu)建 iOS App 的標(biāo)準(zhǔn)模式。Apple 甚至是這么說的。在 MVC 下,所有的對(duì)象被歸類為一個(gè) model,一個(gè) view,或一個(gè) controller。Model 持有數(shù)據(jù),View 顯示與用戶交互的界面,而 View Controller 調(diào)解 Model 和 View 之間的交互。然而,隨著模塊的迭代我們?cè)絹碓桨l(fā)現(xiàn) MVC 自身存在著很多不足。

    • 1)MVC 在現(xiàn)實(shí)應(yīng)用中的不足:

      • 在 MVC 模式中 view 將用戶交互通知給控制器。view 的控制器通過更新 Model 來反應(yīng)狀態(tài)的改變。Model(通常使用 Key-Value-Observation)通知控制器來更新他們負(fù)責(zé)的 view。大多數(shù) iOS 應(yīng)用程序的代碼使用這種方式來組織。
    • 2)愈發(fā)笨重的 Controller:

      • 在傳統(tǒng)的 app 中模型數(shù)據(jù)一般都很簡(jiǎn)單,不涉及到復(fù)雜的業(yè)務(wù)數(shù)據(jù)邏輯處理,客戶端開發(fā)受限于它自身運(yùn)行的的平臺(tái)終端,這一點(diǎn)注定使移動(dòng)端不像 PC 前端那樣能夠處理大量的復(fù)雜的業(yè)務(wù)場(chǎng)景。然而隨著移動(dòng)平臺(tái)的各種深入,我們不得不考慮這個(gè)問題。傳統(tǒng)的 Model 數(shù)據(jù)大多來源于網(wǎng)絡(luò)數(shù)據(jù),拿到網(wǎng)絡(luò)數(shù)據(jù)后客戶端要做的事情就是將數(shù)據(jù)直接按照順序畫在界面上。隨著業(yè)務(wù)的越來越來的深入,我們依賴的 service 服務(wù)可能在大多時(shí)間無法第一時(shí)間滿足客戶端需要的數(shù)據(jù)需求,移動(dòng)端愈發(fā)的要自行處理一部分邏輯計(jì)算操作。這個(gè)時(shí)間一慣的做法是在控制器中處理,最終導(dǎo)致了控制器成了垃圾箱,越來越不可維護(hù)。

      • 控制器 Controller 是 app 的 “膠水代碼”,協(xié)調(diào)模型和視圖之間的所有交互??刂破髫?fù)責(zé)管理他們所擁有的視圖的視圖層次結(jié)構(gòu),還要響應(yīng)視圖的 loading、appearing、disappearing 等等,同時(shí)往往也會(huì)充滿我們不愿暴露的 Model 的模型邏輯以及不愿暴露給視圖的業(yè)務(wù)邏輯。這引出了第一個(gè)關(guān)于 MVC 的問題...

      • 視圖 view 通常是 UIKit 控件(component,這里根據(jù)習(xí)慣譯為控件)或者編碼定義的 UIKit 控件的集合。進(jìn)入 .xib 或者 Storyboard 會(huì)發(fā)現(xiàn)一個(gè) app、Button、Label 都是由這些可視化的和可交互的控件組成。View 不應(yīng)該直接引用 Model,并且僅僅通過 IBAction 事件引用 controller。業(yè)務(wù)邏輯很明顯不歸入 view,視圖本身沒有任何業(yè)務(wù)。

      • 厚重的 View Controller 由于大量的代碼被放進(jìn) viewcontroller,導(dǎo)致他們變的相當(dāng)臃腫。在 iOS 中有的 view controller 里綿延成千上萬行代碼的事并不是前所未見的。這些超重 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)太過于輕量級(jí)的 Model:

      • 早期的 Model 層,其實(shí)就是如果數(shù)據(jù)有幾個(gè)屬性,就定義幾個(gè)屬性,ARC 普及以后我們?cè)?Model 層的實(shí)現(xiàn)文件中基本上看不到代碼(無需再手動(dòng)管理釋放變量,Model 既沒有復(fù)雜的業(yè)務(wù)處理,也沒有對(duì)象的構(gòu)造,基本上 .m 文件中的代碼普遍是空的);同時(shí)與控制器的代碼越來厚重形成強(qiáng)烈的反差,這一度讓人不禁對(duì)現(xiàn)有的開發(fā)設(shè)計(jì)構(gòu)思有所懷疑。
    • 4)遺失的網(wǎng)絡(luò)邏輯:

      • 蘋果使用的 MVC 的定義是這么說的:所有的對(duì)象都可以被歸類為一個(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)檫@加劇了厚重控制器的問題。那么應(yīng)該放在那里呢?顯然 MVC 的 3 大組件根本沒有適合放這些代碼的地方。

    • 5)較差的可測(cè)試性

      • MVC 的另一個(gè)大問題是,它不鼓勵(lì)開發(fā)人員編寫單元測(cè)試。由于控制器混合了視圖處理邏輯和業(yè)務(wù)邏輯,分離這些成分的單元測(cè)試成了一個(gè)艱巨的任務(wù)。大多數(shù)人選擇忽略這個(gè)任務(wù),那就是不做任何測(cè)試。

      • 上文提到了控制器可以管理視圖的層次結(jié)構(gòu);控制器有一個(gè) “view” 屬性,并且可以通過 IBOutlet 訪問視圖的任何子視圖。當(dāng)有很多 outlet 時(shí)這樣做不易于擴(kuò)展,在某種意義上,最好不要使用子視圖控制器(child view controller)來幫助管理子視圖。在這里有多個(gè)模糊的標(biāo)準(zhǔn),似乎沒有人能完全達(dá)成一致。貌似無論如何,view 和對(duì)應(yīng)的 controller 都緊緊的耦合在一起,總之,還是會(huì)把它們當(dāng)成一個(gè)組件來對(duì)待。Apple 提供的這個(gè)組件一度以來在某種程度誤導(dǎo)了大多初學(xué)者,初學(xué)者將所有的視圖全部拖到 xib 中,連接大量的 IBoutLet 輸出口屬性,都是一些列問題。

  • 示例

    • model
    // BookModel.h
    
       @interface BookModel : NSObject
    
       // 根據(jù)需要使用的數(shù)據(jù)創(chuàng)建數(shù) Modal 數(shù)據(jù)模型屬性變量
       @property(nonatomic, copy)NSString *title;              
       @property(nonatomic, copy)NSString *detail;
       @property(nonatomic, copy)NSString *icon;
       @property(nonatomic, copy)NSString *price;
    
       + (instancetype)bookModelWithDict:(NSDictionary *)dict;
    
       @end
    
    // BookModel.m
    
       @implementation BookModel
    
       + (instancetype)bookModelWithDict:(NSDictionary *)dict {
    
       BookModel *book = [[self alloc] init];
           [book setValuesForKeysWithDictionary:dict];
           return book;
       }
    
       @end
    
    • view
    // BookCell.h
    
       @class BookModel;
    
       @interface BookCell : UITableViewCell
    
       // 創(chuàng)建 Cell 視圖包含的內(nèi)容,Cell 使用 xib 創(chuàng)建 
       @property (weak, nonatomic) IBOutlet UIImageView *iconView;         
       @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
       @property (weak, nonatomic) IBOutlet UILabel *detailLabel;
       @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
    
       // 創(chuàng)建 Cell 視圖賦值方法
       @property (nonatomic, strong) BookModel *bookModel;
    
       @end
    
    // BookCell.m
    
       // 包含數(shù)據(jù)模型頭文件
       #import "BookModel.h"                                   
    
       @implementation BookCell
    
       // 從 Model 數(shù)據(jù)模型中取出數(shù)據(jù)更新 View 的內(nèi)容
       - (void)setBookModel:(BookModel *)bookModel {
    
           _iconView.image = [UIImage imageNamed:bookModel.icon];
           _titleLabel.text = bookModel.title;
           _detailLabel.text = bookModel.detail;
           _priceLabel.text = bookModel.price;
       }
    
       @end
    
    • controller
     // ViewController.m
    
         // Modal 模型處理
    
             // 聲明數(shù)據(jù)源
             @property (nonatomic, strong) NSArray *myDataArray; 
    
             // 加載模型數(shù)據(jù)
             - (NSArray *)myDataArray {
    
                 if (_myDataArray == nil) {
    
                     NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"bookData" 
                                                                                                       ofType:@"plist"]];
    
                     NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:array.count];
                     [array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
                         // KVC
                         BookModel *bookModel = [BookModel bookModelWithDict:obj];                               
                         // 使用 Modal 數(shù)據(jù)模型初始化數(shù)據(jù)源數(shù)組
                         [arrayM addObject:bookModel];
                     }];
    
                     _myDataArray = [arrayM copy];
                 }
                 return _myDataArray;
             }
    
         // View 視圖處理
    
             UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, 
                                                                                     self.view.bounds.size.height - 20)];
             myTableView.delegate = self;
             myTableView.dataSource = self;
             [myTableView registerNib:[UINib nibWithNibName:@"BookCell" bundle:nil] forCellReuseIdentifier:@"BookCell"];
             [self.view addSubview:myTableView];
    
             - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
                 return [self.myDataArray count];
             }
    
             - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
                 return 80;
             }
    
             - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
                 BookCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BookCell" forIndexPath:indexPath];
    
             // 從 Modal 數(shù)據(jù)模型中取出數(shù)據(jù)更新 View 的內(nèi)容
                 cell.bookModel = self.myDataArray[indexPath.row];
    
                 return cell;
             }
    

MVVM

iOS 關(guān)于MVVM Without ReactiveCocoa設(shè)計(jì)模式的那些事 - 簡(jiǎn)書

  • 介紹

    • 一種可以很好地解決Massive View Controller問題的辦法就是將 Controller 中的展示邏輯抽取出來,放置到一個(gè)專門的地方,而這個(gè)地方就是 viewModel 。MVVM衍生于MVC,是對(duì) MVC 的一種演進(jìn),它促進(jìn)了 UI 代碼與業(yè)務(wù)邏輯的分離。它正式規(guī)范了視圖和控制器緊耦合的性質(zhì),并引入新的組件。他們之間的結(jié)構(gòu)關(guān)系如下
    • 在MVVM 中,view 和 view controller正式聯(lián)系在一起,我們把它們視為一個(gè)組件
    • view 和 view controller 都不能直接引用model,而是引用視圖模型(viewModel)
    • viewModel 是一個(gè)放置用戶輸入驗(yàn)證邏輯,視圖顯示邏輯,發(fā)起網(wǎng)絡(luò)請(qǐng)求和其他代碼的地方
    • 使用MVVM會(huì)輕微的增加代碼量,但總體上減少了代碼的復(fù)雜性

    *
    76051646.png
  • M:模型(Model)

    • 和MVC中的model保持一致,完全取決于你的"偏好設(shè)置"。你可能會(huì)為model封裝一些額外的操作數(shù)據(jù)的業(yè)務(wù)邏輯,雖然蘋果是推崇你這么干的,但是筆者認(rèn)為不妥,這樣很可能會(huì)導(dǎo)致一個(gè)胖Model的產(chǎn)生,而且胖Model相對(duì)比較難移植,胖Model隨著產(chǎn)品的迭代會(huì)更加的Fat,最終難以維護(hù),一胖毀所有。我更傾向于把它當(dāng)做一個(gè)容納表現(xiàn)數(shù)據(jù)-模型(data-model)對(duì)象信息的結(jié)構(gòu)體(瘦Model),并通過一個(gè)單獨(dú)的管理類來維護(hù)/創(chuàng)建/管理模型的統(tǒng)一邏輯,又或者可以通過使用Category來擴(kuò)充業(yè)務(wù)邏輯。MVVM是基于胖Model的架構(gòu)思路建立的,然后在胖Model中拆出兩部分:Model和ViewModel(PS:感覺是否有點(diǎn)道理)。
  • V:視圖(view)

    • 由 MVC 中的view和 controller 組成,負(fù)責(zé) UI 的展示,綁定 viewModel中的屬性,觸發(fā) viewModel 中的命令以及呈現(xiàn)由viewModel提供的數(shù)據(jù)。
  • VM:視圖模型(viewModel)

    • 千萬不要把它與傳統(tǒng)數(shù)據(jù)-模型結(jié)構(gòu)中模型混為一談。 它的職責(zé)之一就是作為一個(gè)表現(xiàn)視圖顯示自身所需數(shù)據(jù)的靜態(tài)模型;但它也有收集, 解釋和轉(zhuǎn)換那些數(shù)據(jù)的責(zé)任。它是從 MVC 的 controller 中抽取出來的展示邏輯,負(fù)責(zé)從 model中獲取 view 所需的數(shù)據(jù),轉(zhuǎn)換成 view可以展示的數(shù)據(jù),并暴露公開的屬性和命令供 view 進(jìn)行綁定
  • 優(yōu)點(diǎn)

    • 減輕了控制器的負(fù)擔(dān),可以在VM中寫網(wǎng)絡(luò)層的邏輯,用戶輸入驗(yàn)證邏輯,視圖顯示邏輯等
  • 注意

    • view 引用viewModel ,但反過來不行(即不要在viewModel中引入#import UIKit.h,任何視圖本身的引用都不應(yīng)該放在viewModel中)(PS:基本要求,必須滿足)
    • View持有ViewModel的引用,反之沒有
    • ViewModel持有Model的引用,反之沒有


      8fe4617c.png

MVP

在iOS 上實(shí)現(xiàn)MVP(附代碼) - 簡(jiǎn)書

  • M:模型(Model)
  • V:視圖(view)
  • P:主持(Presenter)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目前常用的幾種設(shè)計(jì)模式:代理模式、觀察者模式、MVC模式、單例模式、策略模式、工廠模式、MVVM 1.代理 場(chǎng)景:...
    簡(jiǎn)而精閱讀 818評(píng)論 0 0
  • - ios開發(fā)學(xué)習(xí)中,經(jīng)常弄不清楚ios的開發(fā)模式,今天我們就來進(jìn)行簡(jiǎn)單的總結(jié)和探討~ - (一)代理模式 應(yīng)用場(chǎng)...
    武一頂頂閱讀 1,687評(píng)論 0 4
  • 在說MVVM之前,首先要了解我們最常用的MVC設(shè)計(jì)模式?? 1.MVC設(shè)計(jì)模式 蘋果官方將MVC設(shè)計(jì)模式作為iOS...
    鐵頭娃_e245閱讀 6,108評(píng)論 0 17
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,820評(píng)論 28 54
  • 人工智能是什么?什么是人工智能?人工智能是未來發(fā)展的必然趨勢(shì)嗎?以后人工智能技術(shù)真的能達(dá)到電影里機(jī)器人的智能水平嗎...
    ZLLZ閱讀 4,095評(píng)論 0 5

友情鏈接更多精彩內(nèi)容