MVVMDemo
记录了我对MVVM
架构的一些理解及成长。
一开始进行开发的时候,都是采用MVC
的架构。
在iOS
开发中,UIViewController
是一个相当重要的角色,它是一个个界面的容器,负责接收各类系统的事件,能够实现界面转场的各种效果,配合NavigationController
等能够轻易的实现各类界面切换。在实践中,我们发现UIViewController
和View
往往是绑定在一起的,比如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),block,target/action这些盲通信方式。
对于一个界面来说,有时候View
和ViewModel
往往不止一个,MVVM
也可以组合使用:
ViewModel的哲学
View
不应该存在逻辑控制流的逻辑,View
不应该对数据造成操作,View
只能绑定数据,控制显示。View
并不知道ViewModel
具体做了什么,View
只能通过ViewModel
知道需要View
做什么。Model
应当被ViewModel
所隐藏,ViewModel
只暴露出View
渲染所需要的最少信息。
MVVM双向绑定
Model—>View
:Model
变化时,ViewModel
会自动更新,而ViewModel
变化时,View
也会自动变化。这种流向很简单,你请求数据之后,通过Block
的回调,最终更新UI
。View—>Model
:View
触发事件,更新对面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
参考文章
在学习实践的过程中,看了很多前辈的文章,以下是我觉得写得非常好的几篇,我文中的一些内容也是从他们文章摘取的。