iOS架构由浅入深 | MVVM


前言

本着大道至简,由浅入深的想法。本文会从一个简单的例子入手,逐步解析MVVM在iOS中的应用。说一说看法,比一比优劣,如有不足之处,还望各路大神耐心指出,晚辈不胜感激!

文章目录
  • 架构之争
  • MVVM初探
  • 工程实践
  • 总结

一.架构之争

苹果官方其实是推荐使用MVC,结构大致如下:

MVC示意图

可以看出ViewModel事实上是没有交互的,由Controller负责ModelView之间的交互,交互越多,Controller就越臃肿,更别提实际运用中有些还去掉了View层或者Model层。目前对MVC架构划分是Model作为数据管理者View作为数据展示者Controller作为数据加工者

然而在iOS中Controller中由于有苹果内定的一些视图的生命周期在里面,比如viewDidLoad等等,于是就出现了一些关于iOS的MVC架构方面的争论,有些认为在iOS开发中并没有什么ViewController,只有Model+ViewController;个人比较推崇Casa Taloyum的划分:

M应该做的事:
1.给ViewController提供数据
2.给ViewController存储数据提供接口
3.提供经过抽象的业务基本组件,供Controller调度

C应该做的事:
1.管理View Container的生命周期
2.负责生成所有的View实例,并放入View Container
3.监听来自View与业务有关的事件,通过与Model的合作,来完成对应事件的业务。

V应该做的事:
1.响应与业务无关的事件,并因此引发动画效果,点击反馈(如果合适的话,尽量还是放在View去做)等。
2.界面元素表达

严格意义来说Controller确实做了视图相关的操作,但这个是苹果封装给开发者的视图容器,暴露一些模板方法方便调用,我们应该是在这个基础上进行iOS的MVC架构开发吧😝(PS:个人看法,随便唠唠);
至于从MVC演变过来的MVVM,则做了进一步的优化:

MVVM示意图

抽出了ViewModel层负责数据与视图的交互部分,Controller仅协调各个部分的绑定关系以及必要的逻辑处理,具体各个模块之间的分配借用ReactiveCocoa和MVVM,简介的一张图:
视图层级功能展示图

介绍到这里,想必大家对MVC和MVVM有了一些基本的了解,具体要用什么架构大家各取所需,真正实现所选架构。


小结一下
介绍一下两者对比结果:
MVC

优点:
通用架构;
处理耦合度高的逻辑方便;
缺点:
耦合度高;
复用性差;
测试性差;

MVVM

优点:
耦合度低;
复用性高;
测试性高;
层次更清晰;
重构成本低;
缺点:
处理耦合度高的逻辑比较复杂;
若加入RAC,增加学习成本;
一些Bug比较难调试;

二.MVVM初探

笔者选用RAC实现MVVM架构,当然不是必要的,重要的实现架构,用到的一些库都算是工具,也可以自己用KVO实现,原生的KVO实现会遇到一些iOS下KVO使用过程中的陷阱,还有诸如父类被子类KVO方法覆盖,收到监听消息的判断过于冗长等等;这里推荐使用Facebook开源的KVOController 框架,跟示例差不多,就不重复列举了;

好了,👇下面开始写代码了~
工程结构如下:


MVVM案例一

2.1 ZBMVVMSimpleViewController

协调viewModel绑定model,view绑定viewModel;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //初始化
    self.simpleModel.name = @"帅斌";
    
    //创建视图
    [self.view addSubview:self.simpleView];
    
    /*绑定关系*/
    //viewModel绑定model
    [self.simpleViewModel bindModel:self.simpleModel];
    //view绑定viewModel
    [self.simpleView bindViewModel:self.simpleViewModel];   
}

2.2 ZBMVVMSimpleView

创建视图,实现绑定viewModel的内部逻辑;

- (instancetype)init
{
    self = [super init];
    if(self){
        self.frame = [UIScreen mainScreen].bounds;
        self.backgroundColor = [UIColor whiteColor];
        
        self.nameButton = [UIButton buttonWithType:UIButtonTypeSystem];
        _nameButton.frame = CGRectMake(0, 0, 100, 50);
        _nameButton.center = CGPointMake(self.frame.size.width / 2.0, (self.frame.size.height / 3.0 * 1));
        _nameButton.backgroundColor = [UIColor blackColor];
        [_nameButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_nameButton addTarget:self action:@selector(nameButtonAction) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:_nameButton];
    }
    return self;
}
//按钮点击方法
- (void)nameButtonAction
{
    if(self.viewModel){
        [self.viewModel changeButtonTextAction];
    }
}
//绑定viewModel
- (void)bindViewModel:(id)viewModel
{
    self.viewModel = viewModel;
    @weakify(self);
    [[RACObserve(self.viewModel, nameStr) ignore:nil] subscribeNext:^(id  _Nullable x) {
        @strongify(self);
        [self.nameButton setTitle:x forState:UIControlStateNormal];
    }];
}

2.3 ZBMVVMSimpleViewModel

ZBMVVMSimpleViewModel.h部分:
对外暴露的一些可供调用的接口:

@interface ZBMVVMSimpleViewModel : NSObject

@property (nonatomic, strong) NSString *nameStr;

//绑定model
- (void)bindModel:(id)model;
//按钮点击方法的实现
- (void)changeButtonTextAction;

@end

ZBMVVMSimpleViewModel.m部分:
实现绑定model,按钮更换name;

@interface ZBMVVMSimpleViewModel()

@property (nonatomic, strong) ZBMVVMSimpleModel *model;
@property (nonatomic, assign) BOOL              isClick;

@end
@implementation ZBMVVMSimpleViewModel
//绑定model
- (void)bindModel:(id)model
{
    self.model = model;
    self.nameStr = self.model.name;
}

//按钮点击方法的实现
- (void)changeButtonTextAction
{
    _isClick = !_isClick;
    if(_isClick){
       self.model.name = @"火之玉";
    }else{
       self.model.name = @"帅斌";
    }
    self.nameStr = self.model.name;
}

@end

通过这个简单的案例,可以看出MVVM各个部分之间的关系以及如何实现这一架构;

MVVM的ModelView没有交互,交互移步到ViewModelView持有ViewModelViewModel持有Model,反过来持有的话View容易直接跟Model容易产生耦合,这样就失去了架构的意义;

小结一下:

MVVM的核心在于:(个人意见)
1.MVVM的双向绑定;
2.ModelView解耦;

三.工程实践

参照iOS MVVM+RAC 从框架到实战自己实现了个小demo:

MVVM案例二

看了网上一些实现案例,最后选择了这种结构清晰,又方便管理的工程模式,顺便告诉作者一句,已点赞,已star。

这里原本想抽出tableViewdataSource父类,但看到网上一个比较好的案例,基于MVVM,用于快速搭建设置页,个人信息页的框架,也挺有意思的,学习了。

说一下发现的一个小问题,Masonryblock中使用了weak

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

通过源码可以看出Masonry的block是一个局部变量,在方法调用后就会释放,不存在相互持有,所以这里可以不用weak的;

总结

MVVM模式一直是热议的话题,在众多语言里都有被模仿。虽然将ViewModel分离了,但是也增加了数据绑定,数据分离的一些代码。总之有利有弊吧,供开发者自由选择。ViewController要想瘦身不光一种模式可以选择,实际开发过程中,可能工程模式已经固定,需要一步步进行代码优化,一下子转MVVM还真的有些困难。唐巧大神被误解的 MVC 和被神化的 MVVM提供了几个ViewController瘦身的思路,值得借鉴。

好了,这次MVVM就分享这么多,以后还会分享更深入更有意思的内容,欢迎探讨~

最后附上工程链接:
点此下载

博文推荐:
iOS应用架构谈 view层的组织和调用方案
ReactiveCocoa 和 MVVM 入门
被误解的 MVC 和被神化的 MVVM
iOS MVVM+RAC 从框架到实战
猿题库 iOS 客户端架构设计
MVVM奇葩说

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容