写在开头

刚接触iOS这行时,对架构这些没什么概念,很多时候是为了实现功能,代码难免写得杂乱不堪,这5年积累让我认识到写代码简洁优雅、层次分明、思路清晰是非常重要的。

谈谈MVC

从做iOS开发开始,接触最多的就是MVC了,我对MVC的掌握是从白胡子老头的讲课中学习来的。首先我们要明确各层的职责,Model(模型层)主要是指数据模型对象,例如Person类,View(视图层)主要是指由视图控件构成的界面,Controller(控制器层)主要负责数据操作、数据在界面上呈现。单个MVC的结构图如下:

从图中我们可以看出各层之间的通信关系以及方式,控制器对象持有了视图对象和模型对象,可以直接访问被持有对象公开的属性和方法,模型对象与视图对象之间不可进行通信,需要一个控制器对象进行间接通信,视图对象并不持有控制器对象,可通过Target-Action来进行通信,当用户操作视图时会发送Action给对应控制器(Target)处理,当控制器需要同步了解视图正在发生的事件(Should、Will、Did)时,视图会以Delegate模式来告知其设为代理的控制器发生了什么变化,视图对象也不会持有数据对象,控制器对象可以将数据对象的属性赋值给视图对象的属性,也可以通过代理模式(DataSource)从设为代理的控制器中实现代理方法返回所需数据,数据对象不会持有视图对象和控制器对象,数据对象发生变化时,控制器对象如需了解,可以通过通知方式(Notification、KVO)来通信,视图对象也可以接收通知,但是违背MVC!(注意:Cell持有Model,也是违背MVC的!)多个MVC配合的结构图如下:

从图中我们可以看出,一个MVC可以作为另一个MVC的子视图部分,某些数据对象之间会发生通信(持有),可能存在全局共享数据对象或者一个数据对象拆分成多个子数据对象来供子MVC使用。以下为多个MVC错乱配合的结构图(难以调试、无法模块化):

谈谈MVVM

对于遵从MVC架构的复杂模块而言,控制器中代码会变得很臃肿,单元测试十分困难,为了解决这个问题,提出了一种新的架构MVVM,相比而言前者更为普及,更容易接受。单个MVVM的结构图如下:

从图中我们看出,MVVM架构将UIView和UIViewController统一归为View(视图层),UIView与UIViewController之间仍是MVC架构中的通信关系,View中的UIViewController会持有ViewModel,View中的UIView会跟ViewModel在UIViewController中利用KVO/ReactiveCocoa实现数据和用户行为绑定,当用户操作界面发起数据变化时,ViewModel会进行相应业务逻辑处理并将新数据更新到界面上,ViewModel持有Model,负责业务逻辑处理后Model数据的更新,Model与ViewModel之间也存在数据绑定,可在其中进行数据加工处理,当Model数据变化时ViewModel数据也要相应变化。

RAC实践MVVM

Demo地址是https://github.com/monkiyang/ReactiveCocoaGo,关键代码摘录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// MKMainViewController.m
...
- (void)learnRACCommand {
//点击登录按钮处理登录请求
//RACCommand绑定UI响应事件
self.loginView.loginButton.rac_command = self.userViewModel.loginCommand;
//RAC()、RACObserve()绑定控件与ViewModel
RAC(self.loginView.statusLabel, text) = RACObserve(_userViewModel, status);
RAC(self.infoView.nicknameLabel, text) = RACObserve(_userViewModel, nickname);
RAC(self.infoView.genderLabel, text) = RACObserve(_userViewModel, gender);
}
...
// MKUserViewModel.m
...
- (void)bindModel {
//RAC()、RACObserve()绑定ViewModel与Model
RAC(self, nickname) = [RACObserve(_userModel, nickname) map:^id(NSString *value) {
if (value.length == 0) {
return value;
}
return [NSString stringWithFormat:@"My nickname is %@, ", value];
}];
RAC(self, gender) = [RACObserve(_userModel, gender) map:^id(NSString *value) {
if (value.length == 0) {
return value;
}
return [NSString stringWithFormat:@"I'm %@.", value];
}];
}
...

参考资料

斯坦福大学公开课:iOS 8开发-[第2集] Xcode 、Swift和MVC
iOS 架构模式-解密 MVC,MVP,MVVM以及VIPER架构(译文)