/MVVMDemo

记录了我对 MVVM 架构的一些理解。

Primary LanguageSwift

MVVMDemo

记录了我对MVVM架构的一些理解及成长。


一开始进行开发的时候,都是采用MVC的架构。

iOS开发中,UIViewController是一个相当重要的角色,它是一个个界面的容器,负责接收各类系统的事件,能够实现界面转场的各种效果,配合NavigationController等能够轻易的实现各类界面切换。在实践中,我们发现UIViewControllerView往往是绑定在一起的,比如UIViewController的一个属性就是view

所以在iOS中,UIViewController在很多时候既包含了View,又包含了Controller,随着项目的不断迭代,UIViewController里的各种业务逻辑,数据逻辑,页面跳转逻辑等等越来越多,导致UIViewController越来越难以维护,成为了人们所说的 Massive View Controller(重量级视图控制器)。

在纯粹的MVC设计模式中,Controller不得不承担大量的工作:

  • 网络 API 请求
  • 数据读写
  • 日志统计
  • 数据的处理(JSON<=>Object,数据计算)
  • 对 View 进行布局,动画
  • 处理 Controller 之间的跳转( push/modal/custom )
  • 处理 View 层传来的事件,返回到 Model 层

这个时候,我想着给ViewController瘦身,于是开始接触了MVVM架构,在这个时候的理解,引入MVVM就是为了把大量原来放在ViewController里的视图逻辑和数据逻辑移到ViewModel里,从而有效的减轻ViewController的负担,以便以后的项目维护。

在MVVM中,UIViewController可以当作一个重量级的View(负责界面切换和处理各类系统事件),而原来Controller里的各种逻辑,放到ViewModel里去处理。

有人会问:把所有的逻辑都放在ViewModel里,不也会造成ViewModel变得臃肿,难以维护吗?

ViewModel可以进行拆分,每个View可以对应一个ViewModel


后来又了解到,MVVM 通常还会和一个强大的绑定机制一同工作,一旦ViewModel所对应的 Model 发生变化时,ViewModel 的属性也会发生变化,而相对应的View也随即产生变化。


然后在看文章的时候看到了猿题库 iOS 客户端架构设计,觉得写得也非常好,也实践了一下。

觉得有帮助的给个Star

MVVM详解

MVVM设计模式中,组件变成了Model-View-ViewModel

MVVM有两个规则

  • View持有ViewModel的引用,反之没有
  • ViewModel持有Model的引用,反之没有

图中,我们以实线表示持有,虚线表示盲通信

那么,何为盲通信呢?简单来说当消息的发送者不知道接受者详细信息的时候,这样的通信就是盲通信。Cocoa Touch为我们提供了诸如delegate(dataSource)blocktarget/action这些盲通信方式。

对于一个界面来说,有时候ViewViewModel往往不止一个,MVVM也可以组合使用:

ViewModel的哲学

  • View 不应该存在逻辑控制流的逻辑,View 不应该对数据造成操作,View 只能绑定数据,控制显示。
  • View 并不知道ViewModel具体做了什么,View 只能通过ViewModel知道需要View做什么。
  • Model 应当被ViewModel所隐藏,ViewModel只暴露出View渲染所需要的最少信息。

MVVM双向绑定

  • Model—>ViewModel 变化时,ViewModel 会自动更新,而ViewModel变化时,View 也会自动变化。这种流向很简单,你请求数据之后,通过Block的回调,最终更新UI
  • View—>ModelView触发事件,更新对面ViewModel里面绑定的数据源,例如登录注册的Textfield,你输入和删除的时候,你的Model字段会对应更新,当你提交的时候,读取ViewModel的字段,就是已经更新的最新数据。

数据单向绑定

// 1.定义一个可绑定类型
class Obserable<T>{
    // 定义一个 Block
    typealias ObserableType = (T) -> Void
    var value:T {
        didSet {
            observer?(value) // block 调用
        }
    }
    // 声明一个 Block 变量
    var observer:(ObserableType)?
    // 绑定数据
    func bind(to observer:@escaping ObserableType) {
        self.observer = observer
        observer(value) // block 调用
    }
    init(value:T) {
        self.value = value
    }
}
// 2.我们扩展UILabel,让其text能够绑定到某一个Obserable值上
extension UILabel{
    var ob_text:Obserable<String>.ObserableType {
        return { value in
            self.text = value
        }
    }
}
// 3.建立一个ViewModel
class MyViewModel{
    var labelText:Obserable<String>
    init(text: String) {
        self.labelText = Obserable(value: text)
    }
}

实现数据单向绑定

let label = UILabel()
label.frame = CGRect(x: 100, y: 100, width: 200, height: 44)
self.view.addSubview(label)
        
// 4.单向绑定
vm = MyViewModel(text: "Inital Text")
//修改viewModel会自动同步到Label
vm.labelText.bind(to: label.o

参考文章

在学习实践的过程中,看了很多前辈的文章,以下是我觉得写得非常好的几篇,我文中的一些内容也是从他们文章摘取的。