漫谈iOS中的MVC

Posted 二进制小镇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了漫谈iOS中的MVC相关的知识,希望对你有一定的参考价值。

↗点「 有意思啊 」关注, ... 」转发

iOS直播会员群申请,详情见底部!

作者介绍:

Hi,我是潘晟。喜欢ios,喜欢优秀的设计。 正走在成为优雅的前端专家的路上。

前言

做过一段iOS开发的开发者们对MVC肯定不陌生。这是Apple推荐的开发iOS应用程序的标准设计模式。

我们从一张图开始,谈谈MVC。

MVC


传统的MVC如上图。将不同的对象划分到三个阵营Model、View、Controller。View负责绘图、接收用户的交互,并将交互以Blind的方式传达到Controller。Controller则负责处理相对应的业务逻辑,并告诉Model更新数据。Model则负责更新数据,并将数据以Blind的方式交给View或者Controller。在传统的MVC中C同时知晓V和M两者的状态和所有信息。View也知晓Model的信息,但Model是不知道其他两者的任何信息的。

这种设计在JavaWeb的开发中被广泛使用。因为View层的页面布局,响应,由js和css完成,jsp此时可以直接拿Model解析数据。。在这种情况下,View和Controller的任务最重,Model更像是一个有数据查询功能的Entity。

iOS中的MVC

漫谈iOS中的MVC

iOS中的MVC和传统的MVC大同小异,小异的地方是对传统MVC的改进。

  • 首先是View层。View依然负责着接收用户交互和图像的绘制,但是,View不再直接处理数据了。而是将数据相关的实现逻辑用协议的形式,交给其他对象(一般是ViewContorller),由这个对象告诉View,View应该如何展现。苹果的TableView、PickerView等View的设计很好地印证了这一点。此时View变成了纯粹的"View",一个"Data Independent"的View,复用性无疑是非常高的。

  • Model在iOS中也扮演着与Web开发中相同的角色。常常被开发者们误当成Entity使用。它负责持有数据,并且有少量的逻辑。在这样的设计下,Model其实是非常瘦的。在iOS中,Model和View是相互分离的,既不相互拥有,也相互不交互,即时是以Blind的模式。当Model有所变化时,常常通过KVO或者Notification的方式通知Controller,而不是View。因此,即时有的时候View中会有Notification的监听者,但也绝对不会是Model发出的Notification的监听者。更何况是,我们在实际开发中,Controller有各种方式来告诉View,你应该显示什么,所以View与Model是可以绝对隔离开的。

  • 最后是Controller。Controller在iOS中被命名为ViewController。从命名就可以看出Controller和View的关系非常密切,它直接拥有View,负责着View的创建,显示,隐藏等等。View的代理,数据源,用户交互的响应,也都由Controller负责。Controller拥有View和Model。知晓他们二者的一切情况,负责将Model的数据解释给View,负责根据View中的用户交互让Model处理数据并让View做出相对应的反馈。并且Controller也负责接收Model发出的Notification,在Model状态改变时,及时让View做出相对应的变化。Controller在iOS开发中扮演了重量级的角色。的在实际的开发过程中,有80%的时间是在ViewController上面做文章。这也直接导致了一些问题...

臃肿的Controller

在以上的MVC中,Model其实大多和业务相关,所以Apple的API设计并没有对这一层有所体现。Apple将诸如didReceiveMemoryWarning的方法都放在了ViewController里面,这让我们有种ViewController什么都可以干的直觉。 生命周期管理、依照View的需求将数据格式化,View的初始化及组织,各种Delegate,界面的跳转等等都在Controller中完成。

正是由于Controller扮演了太过重要的角色。导致Controller在编程过程中变得越来越臃肿。其中的代码动辄上千行。有些其他模块也需要的可以复用的代码只能通过复制黏贴的方式。一旦业务和需求有所修改,就得在混合了各种逻辑的Controller的一堆代码中上下翻找。这对像我一样喜欢偷懒的开发者来说就是噩梦。

网络请求放哪儿

在以上的MVC分类中,并没有涉及到网络请求。而网络请求几乎是每一个App必须的。那么,网络请求应该归类到哪边?稍微思考一下,觉得放在Model里会不错。但是网络请求是异步的,如果网络请求还没返回,Model的生命周期却结束了,那可不是一件好事。当然,肯定不会把请求放在View里...所以,最后网络请求还是放到了Controller中。在与我共事的许多有经验的iOS开发者都是这么选择的。但是,这又加剧了Controller的臃肿。

为Contrller瘦身

So,怎么样优化这一结构呢。首先想到的是,把网络请求先移出来。如果解决了Model的生命周期和网络请求的生命周期问题,网络请求相关的代码就可以放在Model里。前面把网络请求放在ViewController里,那么只要保持Model和ViewController的生命周期一致,就可以将网络请求移动到Model里了。

Bingo,让Model成为ViewController的一个Strong属性。

@interface ShopListController ()

@property (strong, nonatomic) ShopListModel *model;

@end

这个时候Model里可以尽情访问网络请求了。因为Model和ViewController生命周期一直,也不必再担心网络请求着陆点丢失的问题。

@interface ShopListModel : BaseModel

- (void)fetchShopListData;

@end

那么数据的存储和格式化也自然而然地在Model中处理了。同时我们还可以把数据持久化,一些类似于验证用户输入等等与UI不相关的数据处理和计算,都可以放在Model里。

@interface ShopListModel : BaseModel

@property (strong, nonatomic) NSMutableArray *dataSource;

- (void)fetchShopListData;

@end

最后我们在model中网络请求完成的回调里,处理好相关数据整理成包含UI直接可用对象的DataSource,并发送相对应的Notification来通知ViewController对View进行更新。

Model实现:

@implementation ShopListModel

- (void)fetchShopListData

{

...

...

[[NSNotificationCenter defaultCenter] postNotificationName:FETCH_SHOP_LIST_DATA_NOTIFICATION object:nil];

...

...

}

ViewController实现:

- (void)viewDidLoad

{

[super viewDidLoad];

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(didReceiveModelNotification:)

name:FETCH_SHOP_LIST_DATA_NOTIFICATION

object:nil];

}

- (void)didReceiveModelNotification:(NSNotification *)notification

{

if ([notification.name isEqualToString:FETCH_SHOP_LIST_DATA_NOTIFICATION])

{

[self.tableView reloadData];

return;

}

}

此时我们得到了一个功能丰富的且易于复用的Model。如果别的界面也需要相同的数据,我们只需要将这个Model给另一个Controller使用,并注册相对应的Notification监听即可。

MVC进化版 — MVCE

其实我们做的这一系列事情,无非是不断地将代码分离。力求做到一种"原子性"。最终最求的目标就是更高的可维护性、可拓展性和复用性。以这种思维反观上面的MVC,Controller已经变得相对较瘦,但随之而来的是Model又胖了起来。

于是就有人提出了MVCS的模式。S即Store。将网络请求与数据持久化相关的操作,分到Store模块中去。

借鉴微软的MVVM,应用到iOS中的ReactiveCocoa,考虑到View与Controller的紧密联系,将View和ViewController直接划分为View。开辟出一个ViewModel来解决上述问题。在MVVM里,你可以把输入验证,网络请求,数据持久化都放在ViewModel里,Model仍然是Model,不与View直接交互。

其实以上的设计模式,都脱不开MVC的影子,只是在实践中不断借鉴,不断优化。包括我想到的这些解决方案。在了解过MVVM设计模式后,这套解决方案还真颇有点MVVM的味道。在我的解决方案里,Model差不多等于MVVM中的ViewModel,而MVVM中的Model在这边等同于Entity。嗯。。于是我打算给它取个名字叫——MVCE。哈哈。。开个玩笑。(为了下文方便,暂且称为MVCE)。

留下的思考

设计模式这么多,该如何选择?

在实战中,不一定非要抓住某个模式不放,更重要的是理解模式背后的意义,并且灵活运用。比如说在MVCE中,ViewController中还剩下什么呢?生命周期,Delegate,界面跳转,Notification响应。有这么一种情况:

有时候有几个TableView高度相似,想要复用之前ViewController中的代理。

一般来说,我们复制黏贴相关代理方法到另一个ViewController中,并稍微修改一下不同的地方。

以上情况其实可以抽象出一个Adapter,专门放TableView的代理,这样在ViewController中,只需要Import这个Adapter,稍微修改Adapter的一些属性,而不需要一遍遍地复制黏贴代理代码。

当然,这样做的后果,牺牲了少部分原有代理的灵活性——我们可以在ViewController中任意定义TableView,切我们的代码中又多出了一个Adapter类。但好处是,封装性更好了,复用性更好了。

最后

返璞归真,所有的设计模式,都是为了更好地解决问题存在的。每个系统有每个系统固有的复杂度,当设计模式将其细分到一定程度,所做的事情只不过是将复杂度的位置挪了挪。所以,我们要了解设计模式,更要在合理的地方,合理地利用设计模式。诸如App中的关于页面,信息量少,逻辑少。所有逻辑直接放在ViewController里,也不会超过100行代码,那就放吧。

有意思啊

youyisiaApp

需要注意:

1. 设计的直播内容适合有一定iOS自学基础(C + OC +UI)或iOS开发1年左右的朋友;无基础的朋友建议不加入;

3. 我们会尽量考虑将直播录制成视频,但请不要传播影响会员利益;

正式Vip:150元/年

前50名特惠价:100元/年

有意思啊

iOS开发者长按关注

以上是关于漫谈iOS中的MVC的主要内容,如果未能解决你的问题,请参考以下文章

Spring与Web框架(例如Spring MVC)漫谈——关于Spring对于多个Web框架的支持

iOS开发之理解iOS中的MVC设计模式

iOS中的MVC设计模式

浅谈iOS中的MVC MVP MVVM

数据库中间件漫谈——看看云时代,它会走向何方

Python中的迭代器漫谈