iOS架构初探(初级 MVC/MVP/MVVM)

目录:

  • 序言
  • 一、架构的基础
  • 二、架构之MVC
  • 三、架构之MVP
  • 四、架构之MVVM

序言:

架构其实是一个设计上的东西,它可以小到类与类之间的一个交互,也可以大到不同的模块之间,或者说不同的业务部门之间的交互,都可以从架构的层面去理解。

【理解】:iOS 中也有很多架构的设计理念,比如代理,它属于委托设计模式。比如单 例,是一类单个对象长存的设计模式,它不需要传递任何参数,就有效的解决了不同 代码数据共享的问题。还有观察者模式(通知),一般为model层,对controller和view 进行通知,它不关心谁去接收,只负责发布信息。比如常用的 MVC 模式,通过数据模 型,控制器逻辑,视图展示,将应用程序进行逻辑划分等。都是一种架构设计理念, 一种资源有效整合的方式,方法。我们如果对架构熟悉了,也可以用自己的理解去, 研究出属于自己的一套架构,方便日后的工作学习。

一、架构的基础

首先,架构的核心是耦合,如何处理不同的类和业务模块之间交互方式,让它们互相影响最小,就叫做解耦。耦合度低,不仅可以帮助我们有效的维护项目代码,还 可以提高代码的质量。架构的基础,就是对耦合概念的理解。

1、案例:

假设常景: 经理让员工去执行打印任务。
具体操作:
设计两个类来实现代码工程。创建两个类一个是经理,一个是员工。为了简化场景,把两个类都变成单例。员工要执行打印任务,内含 doPrintJob 方法,声明并 实现且打印日志。员工执行完打印任务,需要告诉经理,打印任务完成了。所以经理 内含 celebratePrintDone,方法,声明实现并输出日志。到此为止,经理和员工还是两 个独立的个体。

接下来我们要将二者串联起来,串联起来的方式就是耦合的方式。

第一种,互相耦合(最差)

经理是模块的入口,所以在经理类中,设计接口 beginPrinTask.声明实现且对 doPrintJob 调用,告诉员工开始打印任务,这样入口就做好了。

在 ViewDidLoad 中调用 beginPrintTask 方法进行启动。当员工打印任务完成时, 需要告知经理,则调用经理 celebratePrintDone。如下图:


第二种,利用 Delegate 耦合

苹果使用的都是消息机制,我要员工去打印,就是给员工发送一个消息,其实就是调用员工打印的方法。

使用代理解耦,员工不知道经理的存在,经理一定知道员工的存在,无论是谁,只 要遵守了员工的协议,就代表完成了任务。

当经理告诉员工打印任务之后,员工打印任务完成了,要告知经理,却不知道怎么 去告知经理,则需要一个委托人代替它去告知经理任务完成。怎么去做呢?员工需要 和委托人之间有个协议,去告诉经理 PrintJobDone。

第三种,利用 Notification 耦合

首先,需要生成一个.h 文件,来定义我们通知的类型,一个是开始打印,一个是打印完 成,比如:
#define Notif_BeginPrintTask @"Notif_BeginPrintTask"
#define Notif_PrintTaskDone @"Notif_PrintTaskDone"
经理需要告诉外界我有一个打印的需求,则需要在beginPrintT ask方法中POST消息, 员工呢,去监听这个信息,如果对这个需求信息感兴趣的话,就去执行doPrintJob方法。 打印完成后,需要告诉外界,打印完毕。外界需要监听一下完成这个消息。

我们其实都没有把耦合消灭掉,是把耦合全都移到了一个.h 文件中,将耦合变成 了一个信号,一个事件。

二、架构之MVC

1、 介绍

MVC 是苹果原生推荐的架构方式,也是行内经典的架构模式,几乎所 有的逻辑处理都离不开这三个类。
- M (Model) 代表数据部分
- V (View) 代表视图展示
- C (Controller) 代表控制器
当然我们也可以对它们进行细分,Controller 可以分更多的模块,View 也可以分更多个子 View 进行控制,Model 也可以分多个 Model 类型。

2、讲解


通过上图,我们知道有三个角色,Controller、View、Model,三者之间交互就 决定了架构的耦合程度和便于维护的程度。

Controller 有两个红色的箭头分别指向 View 和 Model。
- Controller->View
就代表 Controller 可以控制 View 的渲染,这样的一个命令。
- Controller->Molde
代表 Controller 可以修改 Model。

另外, View 也有两个绿色的箭头,分别指向 Controller 和 Model。
- View->Controller
代表 View 这边采集到的用户数据,用户事件,会反馈给 Controller 做进一步的处理。
- View->Model
代表 View 可以读取 Model,进一步展示自己的界面。

从这个图形中我们可以指导 Controller 是我们的中心控制器部分,所有的关键业 务逻辑部分都是从 Controller 部分发生的。这就是 MVC 这个架构为什莫这么方便维护 的原因。是因为我们将主要的耦合部分、重要的逻辑部分都放到了 Controller 当中。

3、案例

我们新建一个 MVCController 类,在 ViewController 中 present 出来,设置dispatch_after 2 秒之后展示出来。我们在建立一个 MVCView,创建一个 Model 因为 model 执行一个打印的操作,所以我们建立一个 Paper 作为 model。

这样我们就有个 MVC 三个对象,接下来就来执行我们的交互。在 MVCController 中 ViewDidLoad 中我们会执行一个打印的任务。生成 printPapaer 这样一个函数。接下来我们要是这三个角色串联起来,首先要 先生成我们 View/Model 的对象 myView 和 paper。因为 MVCController 是 控制 View、Model 两个对象的,所以我们需要两个引用。

另外,我们回到 MVCView 当中,view 需要一个操作 printOnView: (Paper*)paper 把这个 View 展示出去,需要传入我们的 Model。View 当中有 一个事件可以告知到我们的 MVCController,所以在定一个协议 MVCViewDelegate,传给 MVCController 一个事件 onPrintBtnClick,声明一 个代理对象。

接下来,我们定义我们的 Paper,它有一个 content 属性,是一个纯 Model 没有业务逻辑。

回到控制器的部分,在 printPapaer 部分,我们需要告诉 View 去打印 这样一个 Paper。在 ViewDidLoad 进行 myView /paper 初始化操作,并在 其中实现一个 MVCViewDelegate,实现 onPrintBtnClick 方法。

执行我们的 printPaper,其实就是告诉我们的 View 去打印。 [self.myView printOnView:self.paper]; 随意设置一个 content 的值,在 printOnView:(Paper*)paper 方法中打印一个日志, NSLog(@"printing
paper content:%@",paper.content);

结论①
测试会打印出来 Line0,这样我们就完成了,Controller 去控制 View 去
展示 Model 内容这样一个流程。

接下来是另一个流程,view 采集到用户的事件,告诉 Controller 去做
一下处理。

在 View 中我们要生成一个 btnPrint 去采集用户事件,在 View,init 中 初始化这个 btnPrint,给 View 增加一个背景色,淡灰色.设置 btnPrint 的属 性 frame、title、添加点击事件 onPrintClick。

在采集到用户事件之后我们需要传递给我们的 Controller,在 Controller中才能做处理决定下一步该怎么做。


执行到上一步就可以将,事件传递给我们的 Controller,Controller 将进 一步改变我们的 Model,之后我们在将指令发送给 View,让 View 去渲染新的 Model 对象。如下图 Controller 遵守代理方法的实现:


结论②
这就是另一个数据流向的一个流程,在我们的 View 采集到数据之后, 将数据抛给我们的 Controller, Controller 修改数据,然后将新的数据, 抛给 View 去展示。


4、总结
- 流程 1 、Controller 初始化 发指令给 View 去展示我们的 model
- 流程 2 、View 通过用户采集到事件之后将,将时间发送给 Controller,
Controller 采集到做进一步的处理之后,刷新我们的 View。

三、架构之MVP

1、 基本介绍
架构有很多,我们最好是在 MVC 的基础上去学习,好多架构都是 MVC 的变种。MVP 也不例外,是 MVC 的一个变种。


上图列举了 MVC 和 MVP,方便大家可以比较一下二者之间的差异。 在之前,我们介绍MVC的时候,通过Controller来控制View和Model, 而且 View 可以获取到 Moedl 并进行自己的更新。

大家可以看一下左边的流程图,User Interaction(用户事件)之后, 是向它传递给 Controller, Controller 改变 Model,Model 之后就会反应到 View 上去。MVC 示意图有一个很重要的点是 MVC 三个角色是互相知 道,互相引用。像 View 是知道 Model 的存在的。

那么,到 MVP 的时候,我们会发现 Controlller 消失了,多出一个 Presenter。大家可以将 Presenter 理解成 Controlller 一个类似的概念。 只不过数据流流向有些变化,所以给它我了一个新的名字叫 Presenter。 但是总的来说,它也属于一个控制中心。

从右边 MVP 中,我们可以看到和 MVC 不一样的地方,View 和 Model 之间,是互相不存在的,不存在引用,不存在依赖,所有的耦合 到放在 Presenter 当中。

回到我们打印的例子来说,Presenter 会控制 View 执行打印任务, View 并不直接持有 Model 的引用。Presenter 会以另外一种形式展示给 View,View 将其打印在自己的 UI 界面上。

2、 案例讲解

打开 Xcode,我们新建一个目录 MVP,创建一个 MVPViewController,因 为 MVPViewController 是苹果 iOS 编程中最基础的单元,是必须要有的, 所以我们还是进一步保留。不过和之前不同的是,我们不会在其中在放 入很多逻辑业务的代码。我们再生成一个 Presenter,生成我们 View 的对象 MVPView 和 MVPModel。

接下来,我们在 MVPViewController 当中将三个角色进行关联,在 ViewDidLoad 当中进行处理,我们需要导入三个头文件,在 interface 当中声明。因为要在 MVPViewController 当中,对三个角色进行串联, 所以我们将三个对象的生成,都放在 MVPViewController.m 文件中。

声明图中三个对象,并进行初始化,如图下:



MVP 当中的 Model,我们还是采取之前的例子,打印 Paper 一样, 如下图:


View 的话他有一个函数 printOnView 方法,实现。Presenter 的话,它有一个方法,是我们业务逻辑的入口,我们打印的一个业务功能 的入口,printTask 并实现。

前面已经介绍,所有的耦合都放在 Presenter 里,Presenter 是需 要知道 View 和 Model 存在的。这里我们将它进行一下声明。有个 View 和 Model 我们就需要把他们进行展示出来。printTask 就是调用 View,并 把 Model 的内容传给它。

因为 View 打印的话,并不知道 Model 的存在,所以我们的内容需 要手动的传过来。跟 MVC 不同的就是并不是直接的将 Model 传进来, 而是将一个 string 传进来。因为 view 并不知道 model 的存在,是不能 引用 Model 这个类的。

接下来我们在 MVPView 当中把它实现,通过一个 Label 把 content 展示出来,我们在 init 当中对其进行初始化。
如下图:


这样的话,View 就基本完成了它的工作,model 就完成了它的定义。 接下来所有的业务逻辑处理都交给 Presenter 来进行处理。

在 Presenter 当中,需要将 content 传递给 View。如下图,这样我们 就完成了一个流程。就是我们启动时的一个业务流程的一个入口,我们 通过 Presenter 来,来指定我们的 View 来打印这一个内容。这是我们的 第一步。


当然,我们也需要在 ViewController 当中,对 MVPViewController 进行实现。showMVP 两秒之后进行展示。并在 MVPViewController 当中 对 Model 进行赋值。

测试一下,并没打印,原因是我们三者的关系还没建立好,我们把他 们进一步建立。Presenter 需要指导 View 的存在,我们把声明的两个属 性 view,和 model 暴露在外部。这样就可以给 MVPViewController 赋值 了。

我们看到了 Presenter 是处理三者耦合的一个类,这样我们在维护我 们的项目,维护我们的代码耦合的时候,就会变的更加的方便。

Presenter 的话,它有一个业务逻辑的入口叫 printTask,在 MVPViewController 当中进行调用。打一个断点进行检测,打印成功。 说明我们的流程是成功的。

接下来我们做另一个工作,就是之前跟 MVC 一样的,在 View 上的按 钮,响应一个点击事件。通知我们的 Presenter 去执行这样一个操作, 这个操作和我们之前 MVC 的比较一致。这里就不做过多解释了。利用代 理。

四、架构之MVVM

1、 基本介绍


MVVM 架构也是 MVC 架构演化出来的,大家可以看到图中有三个角色, View/ViewModel/Model,和 MVC 相比,Controller 没有了,多了一个 ViewModel。

我们可以直观的想到 ViewModel 是不是取代了 Controller 的角色,它不 但取代了 Controller 也多了一些不一样的地方。我们可以从它的名字推断出, 它并不全是一个业务逻辑中一个类,它同时和 View 和 Model 是相关联的, 这也是 MVVM 主要的特征之一。View 和 ViewModel 是双向绑定的。

什莫意思呢?比如说,我们 View 上有一个 label,label 上它的 text 对应 到 Model 里面的一个 name 的 string 字符段。我们的 ViewModel 它也会有一个 next 的 string 字符段。如果 nextString 发生改变的话,nameLabel 它对应的 text 值会自动的更新。同理,若果 View 上的一个 btn 产生事件的话,viewModel 能够自动的监听到,这里是一个双向的过程。iOS 上面有很多方法可以实现, 可以用 RAC 去做,也可以用 KVO 来做,下面的案例会采用 KVO 方式来做,理 解这种双向绑定。

ViewModel 同时也跟 Model 发生一个关系,ViewModel 可以改变 Model 值, 同时,在 Model 发生改变的时候,它可以将 Model 最新的值读取到。并将它反 应到 View 上。

2、 案例(打印)
这里我们新建一个目录 MVVM,我们新建一个 MVVMController,在 MVVM 主要角 色里并没有 Controller,它相当于一个空壳的作用。我们需要简历 MVVM 三个角色, MVVMView、MVVMPaper/MVVMViewModel,这就有个 MVVM 的三个角色。

接下来,我们对三个角色进行初始化,我们将三个角色的头文件,导入 MVVMController,生成三个实例,分别为paper/mvvmView/viewModel,并进行实例化。

对于 MVVMPaper 我们好跟之前一样,有一个 NSString 属性 content,在 MVVMController 给它初始化内容。MVVMView 它有一个 label 去展示 paper 的 content。 在 init 方法中对它进行初始化。

接下来是我们的 MVVMViewModel,之前我们提到了它和 View 之间有一个相互绑定 的关系,所以我们在 MVVMViewModel 的头文件中生成一个 NSString 的 contentStr。 这个 contentStr 是用来和 View 进行双向绑定作用的。

接下来我们需要在 MVVMViewModel 当中,建立一个双向绑定的关系,这个绑定 也是 MVVM 架构一个核心的地方。这里将采用 KVO 来实现。

KVO 我们将采用 FaceBook 中一个著名的库 FBKVO,将它导入进来,大家可以去 网上搜索一下,这个库非常的有名和实用。其实它就是非常简单的告诉我们如何使用 KVO,我们导入头文件 FBKVOController.h,关系应该在 MVVMView 中去建立。
MVVMView 需要和 MVVMViewModel 绑定,则我们需要把 MVVMViewModel 的头文件 导进来。在 MVVMView 当中,它需要一个 setWithViewModel:(MVVMViewModel*) vm,在这个方法里我们建立一个双向的绑定。

这样,我们就完成了一个 View->ViewModel 的绑定,每次 ViewModel 的 contentStr 发生 改变的时候,这样_lbContent 的值就会自动的赋过去。

这样,有什莫好处呢?一旦我们的 View 建立完成之后,我们展示的逻辑就会跟我 们 Model 的改变完全隔离开来。不用操心 View 的值是怎么显示的,我们只要把注意力和重点关注在业务逻辑上,怎么去改变我们的 Model。

下面我们去 MVVMController 里面去调用一下 setWithViewModel:如下图:


这样,我们就可以监听到_paper.content = @"line 0";这样一个值。

接下来我们实现第二个绑定,我们的 ViewModel 需要有一个事件。我们在 MVVMView 中添加一个 Btn,初始化,接下来是我们一个事件的处理。事件的处理非常 简单,因为我们已经有一个 ViewModel 的一个引用了,我们只要将事件传递过去。

所以,ViewModel 需要一个时间的回调,我们在 ViewModel 里声明一个方法, onPrintClick 并实现,在 MVVMView 中设置 ViewModel 的引用,并关联。 在 MVVMView 中调用 onPrintClick 方法,这样就将时间传递给我们的 ViewModel。

Model 的话,接下来我们只要,改变 Content 就可以改变这样一个内容。因为我们 的 ViewModel 还要跟我们的 Model 发生一个关联,所以在 ViewModel 里面将 MVVMPaper 导入进来,并声明 model。我们还需要将 paper 传进来,所以如下图:


并实现,赋值的时候,我们需要将当前的 contentStr 进行赋值。如下图:


因为,之前我们已经建立过绑定,改变 contentStr 的值的时候,View 上就能够自动 的监听到这个值的变化,能够在 label 上进行一个新的值的展示。所以实现下面的方法: 如下图:


在 MVVMController 里,我们需要将 model 传过去,如下图:


与 ViewController 进行处理,Com +R 运行测试一下。
未完待续

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

推荐阅读更多精彩内容