MVC模式是什么
MVC全称Model-View-Controller
- Model:用于管理数据
- View:用于展示数据
- Controller:用于管理事件
根据唐巧的文章介绍,MVC的概念最早出现在二十世纪八十年代的施乐帕克实验室中,就是传说中乔布斯抄袭的那个实验室。其本质是尽量高复用View和Model.
- View: 你如果抽象得好,那么一个 App 的动画效果可以很方便地移植到别的 App 上
- Model: 用来存储业务的数据的,如果做得好,它也可以方便地复用。
- Controller: 因为一个界面逻辑对应一个Controller,所以很难被复用
当意识到Controller很难被复用,那么设计的理念就清晰了,容易或者说需要复用的放入View和Model,Controller里尽量不要放需要复用的代码,因为Controller本身不便于复用.
Xcode中的MFC
根据苹果推荐的MFC建立方法
- Model: Xcode并没有推荐的,一般使用自建一个基于NSObject的类来当作Model
- View: Xcode推荐使用的View是xib和Storyboard,两者实质是XML文件
- Controller: Xcode最常见的就是ViewController类,用于控制View文件的声明周期和事件
在Xcode推荐的方式里,一般使用@IBOutlet进行关联View和Controller.但是由于好多人不喜欢使用xib和Storyboard来书写View,加上遇到批量更改或者同一管理时,使用代码更加方便,所以导致实际情况是View的内容和ViewController的内容混合在了一起.
Xcode中Model的常用方法
由于Model是自建的一个NSObject类,一般使用属性来保存数据
@property (nonatomic, copy) NSString *theme;
然后会自定义以下方法用于模型的初始化
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)createWithDict:(NSDictionary *)dict;
第一个方法是通过字典初始化模型的实例方法
- (instancetype)initWithDict:(NSDictionary *)dict {
self = [super init];
if (self) {
self.theme = [dict valueForKey:@"THEME"];
}
return self;
}
第二个是使用字典初始化模型的类方法
+ (instancetype)createWithDict:(NSDictionary *)dict {
return [[self alloc]initWithDict:dict];
}
如果需要对模型进行深拷贝,需要实现NSCopy协议
- (id)copyWithZone:(NSZone *)zone {
CustomModel *newItem = [[CustomModel allocWithZone:zone] init];
newItem.theme = self.theme;
return newItem;
}
还可以实现逆转换方法,用于转化成通用的Dictionary或者Array
+ (NSDictionary *)convertDictFromModel:(CustomModel *)model {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:model.theme forKey:@"THEME"];
return dict;
}
View和Controller混合在一起造成的问题
由于纯代码完成View造成了ViewController.m这个文件中会有一大部分代码是界面,这部分代码完成了本来应该用Xib和Stroyboard完成的工作,所以代码量的增加,会导致Controller显得特别臃肿,不利于代码Review和审查
为了解决这个问题,才会有人提出了MVVM这个新的的设计模式,以及我目前更喜欢的另外一种模式来解决.
MVC的理解
MVC的误区
我个人对于MVC最早的理解,认为MVC应该是完全分离的,像圈地一样把不同的东西放在不同的地方,甚至认为只要我有独立的数据管理机制,就是Model,所以我把所有界面之间的数据都通过字典和数组储存和传递.
MVC误区带来的问题
之所以我通过数组和字典进行储存数据和传递,基于的考虑是字典和数组不管在任何界面都是可以被解析的,不用加载一个自己的Class进行解析.
但是这引起了很多问题:
- NSDictionary和NSArray本身是不可变的,一旦init就不能对其内存进行修改
- 使用Key-Value键值对调用,很容易写错Key导致数据出错
- 每个界面都要自己解析Dictionary是什么
虽然以上三个问题都可以通过技术手段避免,但是这明显进入了为了解决一个简单的问题,引入了更复杂问题的怪圈.而最关键的问题是,使用Dictionary会影响到MVC三个模块之间的联动.
MVC的核心:联动
但是只所以被称为设计模式,其实MVC三个部分之间并不是说要做到去耦合,完全独立的关系.MVC之间应该是一种联动关系,最终为了是通过不同的方式来展示信息(数据),如下图.
Model可以作为Controller的一个属性,通过**setModel:**方法进行控制,每当Model数据被Set时就会引起Controller对View的控制
而用户对View的改变,会通过Action作用于Controller,在Action触发的函数里实际改变(Change)Model的数据.
而这一切联动,都是围绕着信息,所以说最终用户和计算机之间交流的本质就是信息.
误区不能联动举例
- 需求
在完成一个读取本机号码做成TableView的过程中,可以通过点击Cell改变当前对象为选中或非选中状态
- 问题1: NSDictionary无法改变
由于NSDictionary一旦init就无法改变,所以当根据动作不同改变名为”Selected”的Key值时,无法改变,这个问题可以通过NSMutableDictionary解决,但是还存在问题2
- 问题2: 无法通过Set函数联动
解决了问题一之后,在滑动TableViewCell,由于存在TableViewCell复用
- 在第一行被选中,把索引第一的NSMutableDictionary的”Selected”的Key值改变
- 但是Cell再复用后又从选中后的View样式变回了默认的未选中的View样式
- 产生的原因是复用出的Cell并没有接受到Selected这个动作.
如果要使用字典和View联动,还需要自己写一套联动方法,并没有直接通过读取自身属性Model的值快.就犯了为了解决一个问题引入另外一个问题的悖论.
- 结论
综上所述,使用自定义的基于NSObject的Model更利于理解和使用,虽然Array和Dictionary有较好的通用性,但是并不方便.