让人点碎苹果手机屏幕碎了的Nice如何做iOS客户端架构

1.4k 人阅读
标签:至少1个,最多5个
极牛技术实践分享活动
极牛技术实践分享系列活动是极牛联合顶级VC、技术专家,为企业、技术人提供的一种系统的线上技术分享活动。每期不同的技术主题,和行业专家深度探讨,专注解决技术实践难点,推动技术创新,每周三20点正式开课。欢迎各个机构、企业、行业专家、技术人报名参加。
刘诗彬,毕业于北京邮电大学电子信息科学与技术专业。曾在创业公司工作2年,12年加入新浪微博,资深iOS工程师,作为核心团队成员参与了微博iOS客户端的重构和发展。2014年加入Nice图片社交app,担任iOS技术负责人。
万事开头难,当你开始着手设计并实现iOS客户端架构的时候,很可能会出现暂时无从下手的情况。那么,一个创业产品的iOS客户端架构到底怎么做呢?现下最有活力的图片社交软件Nice的技术负责人刘诗彬将为我们解答创业产品如何实现iOS客户端架构。
01/创业项目的特点和问题
作为创业产品,不管从产品、技术、团队、节奏还是业务特点等都与相对成熟的大产品有有很大的区别。首先分析一下创业公司做产品的一些特点:
1.团队不够成熟成熟的产品团队会有一套共同的做事方式,新人会慢慢被同化,而创业公司很多时候都是在较短时间的把大家召集在一起,团队需要融合到一起,所以找到适合的共同做事方式是非常重要的。
2.基础建设不够完善作为一个新的产品,有很多基础工作,从技术框架,协作方式甚至到代码风格也需要逐渐建立起来的,为了支撑产品健康发展的基础建设是很重要的。
3.产品不够稳定作为一个创新产品,在产品迭代过程中可能有各种的创新和尝试,反映到技术实现上就是经常会有各种的需要变更,代码可能会需要经常重构,为了能比较好的适应产品的节奏,在架构和具体实现设计上需要慎重考虑。技术团队需要能很好的支持这些变化和调整,做到最高的效率和效果,否则很容易在这个过程中欠下很多债,最后会慢慢累积到拖整个产品的后腿。
4.迭代速度快大家都能看到现在市场的节奏,创业就是在和时间赛跑,快速的迭代是基本的原则。从微博的一个半月一个版本到现在的一到两周一个版本,不同的情况需要不同的做事方式,设计一个支持快速变化的框架来协调好不同优先级的事情是很重要的。
02/基于创业特点的技术框架设计和分析
针对前面提到的特点,我们慢慢总结出一些方案来适应,总体来讲就是:
1.技术核心团队是做事的根本,保证团队对基本技术思想和方案的共同认识非常重要。在快速变化的产品节奏中,如果团队有高度的默契,往往能专注在业务本身,做到事半功倍。如果不能统一大家的认识,沟通会遇到不少问题,项目可能越来越繁杂,各个模块会越来越难以维护,最后会浪费很多时间在不必要的管理上。
2.可扩展性创业产品初期大多还是以需求为核心的项目,在设计实现方案时需要把为适应产品需求的扩展性放在较高的位置,要有适应环境和需求变化的能力。
3.集成性创业产品一般都有一个确定的产品核心价值,核心体验是一个产品成功的关键,在考虑扩展性同时不能脱离这个产品核心,怎么围绕核心产品功能来协调各模块的实现也很重要。要把各模块能有效的结合起来,发挥最好的产品效果。
4.可成长性各部分能独立成长优化,适应产品的快速发展。到具体方案实现上总结为以下几个方面:
1.层次化最开始接手的时候我们的项目没有模块的和模型的概念,各种基础功能可能混杂在各种具体业务里,业务调整和扩展非常困难,例如没有数据模型,不能统一解析。第三方服务没有统一,各个地方各自实现。账户信息没有统一管理,状态很难同步。在把能独立和抽象的逻辑各自整理后,按照不同的特点我们把项目总结为三层:技术基础:这一层是与业务无关的支撑一个项目开发的基础,虽然不涉及到具体业务,但是他决定了项目的基础技术指标。业务支持:这一层实现了最基础的业务逻辑,一般一个项目的最核心的功能不会改变,这一层的实现基本体现了项目最基本的核心逻辑,实现相对也比较稳定。具体业务:这是真正的各种具体功能,基于下面的基础,在实现具体业务时能专注于具体功能的开发,根据产品功能的划分,实现上也能再具体化分为各个模块。不同的层次有不同的特点,基础要求稳定高效可扩展,具体业务要求用户体验和开发效率,根据不同的特点在实现上就可以对症下药,设计不同的方案。只要定义好各个层次之间和模块之间的接口就能很方便的进行协同开发。按照不同的特点,团队分工合作上也可以更合理的协调不同的资源。
2.模块化其次是根据刚才的分层,我们同时已经把项目分为了各个模块,根据特点各个层次的模块也有不同的设计思想。首先,定义好大的框架,在创业阶段,框架可能只需要满足简单清晰,高效和快速,但是保留足够的扩展性。只要保证好确定的接口,基础框架还可以独立进行优化和调整。然后是对业务模块的划分,不同的业务模块可能也有不同的特点,比如有的注重效率有的注重交互的体验,有的注重扩展性。很多时候我们讨论的设计模式可能都属于这一层,类似MVC,MVP,MVVM,其实我觉得不同的模块不一定要完全统一,适合最重要。在我们的项目中,大部分业务逻辑本身内容都没有很大变化,但是交互和UI会经常有较大调整,所以我们把各模块的接口交互和数据处理等确定逻辑抽象出来,提供出特定的接口,我们称之为“Engine”,UI则专门拿出来作为适配,只要满足固定的接口就能和对Engine对接,协调和组织的的工作则放在Controller,逐渐形成了Controller-Engine-Model-View的模型。最后是具体业务的实现细节,一般前面两个层次是需要相对稳定的,由于涉及到团队合作,在具体细节上可能就很难保证统一了,具体情况具体分析是这一层的方法,至少可以把差异性和风险都控制在很具体的点上。
3.插件化为了做到前面所提的适应性和扩展性,很多时候我们会用到插件化的设计。以微博发布系统基本结构为例,首先发布内容这个基本功能本身基本不会有大的变化,所以抽象出了负责管理数据和逻辑流程的发布队列和负责交互和UI框架的发布器容器。但是,随着业务的发展和各种产品层面的调整发布的内容和形式常常会有较大的变化,为了使各种逻辑更加清晰,每种内容可以独立成一个子模块,他们自己可能有自己的交互、UI和数据等逻辑。
03/实践经验重点总结
接口设计为了把这些模块比较方便的结合在一起,便抽象出一个通用接口与发布器交互,这样不管单独模块怎么调整一般都不会影响整体的逻辑。也可以做到方便的扩展和业务调整。涉及到模块间和模块内结合需要用到对应的接口形式。接口形式有很多种,经常会有不少人习惯性选择某种特定形式,但是其实并没有那么合适。其实每种形式都有自己的特点,需要针对实际情况选择,这往往是需要注意的。在实际开发过程中我们还是会经常遇到之前的设计或者实现不能满足最新的需求,这时候就需要涉及到重构。怎么在保证产品节奏的同时做到及时的做好合适重构是经常遇到的冲突。
何时重构我们经常可能会因为自己的感觉来觉得那些实现不爽就想要重构,其实重构之前还是应该多做好准备。重构要满足三个条件:实现新需求时;修复bug时;有足够时间来优化。那么何时要避免重构呢?没有产品生命里的模块,时间不能保证的时候,没有较为明确的预期时,都是要尽量避免重构的。在实际项目中不只是代码逻辑,整个流程需要很多基础设施来保障,我总结为三部分:首先是业务流程 从产品需求提出到评审开发,再到测试回归最后检验产品效果,整个流程需要有比较清楚的流程控制来保障高效的执行。然后到具体的开发中其实有很多东西可以用来提高效率,开源工具,自动化脚本,基础工具等等。
基础设施建设
1.业务流程管理
除了代码实现外开发流程中还会涉及到很多必要流程,打包分发测试,Crash和性能收集分析等。上面是我们用到过的一些流程管理工具:Wiki:Trello:JIRA:Tower:我们使用Wiki来管理产品文档,技术规划,项目排期等文档,能比较方便的跟踪和多人协作。Trello能很方便的跟踪工作流程和任务协作以及时间计划。JIRA是也是任务跟踪,但是一般大家都用来作为bug管理,因为他的责任跟踪和流程管理非常明确。以前是试用过Tower,是国产的一个协作和任务管理工具,前面三个功能他都有,但是可能没有每个专业的那么细化。各种工具都有优缺点,选择一个适合的工具能保证流程的控制和效率。
2.代码和实现在开发过程中只要留心会有很多细节可以优化,甚至能极大的提高质量和效率。比如现在在ARC环境下其实经常有人会忽略内存管理的问题,通过一些简单的代码就能帮助发现可能的内存泄露。
很多代码逻辑可以抽象出来实现自动处理,往往能极大的优化成本和性能,比如我们结合项目实际和各种开源项目实现的自动解析和序列化数据。
随着项目快速的发展和变化,项目很容易变得臃肿,中间可能有很多无用的资源和逻辑,通过一些简单的脚本就能发现项目里的无用资源和逻辑。最近我们通过脚本检查和整理项目让安装包减少了十几M。
Crash统计分析,BUG修复,动态配置等都有很多成熟的解决方案,也可以根据项目实际自己优化。
3.辅助工具除了代码外还有很多事情可以提高工作效率,有不少现成的工具能使用,如果需要也可以开发适合自己的辅助工具。
在我们的项目中,节奏非常快,经常会有各种尝试demo或者需求需要测试review。之前的打包测试的效率非常低,首先是开发需要花很多精力在打包上,然后需要用各种方式把安装包分发给不同的人,然后还要跟踪反馈。所以我们开发了一套自动化打包平台省去打包和分发跟踪的成本。包括Xcode套件里有很多功能和工具能帮助发现解决问题和提高效率,善于使用会受益无穷。其他类似Charles,Sketch等工具也是应用广泛。
*此分享由Nice企业的刘诗彬在极牛线上技术分享群里所分享,有意加入的技术朋友,请在极牛公众号(ji-niu)里回复“技术分享”。
0 收藏&&|&&3
你可能感兴趣的文章
你可能感兴趣的文章
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。iOS 客户端架构设计 - 简书
iOS 客户端架构设计
转载自:序猿题库是一个拥有数千万用户的创业公司,从2013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代。在如此快速的开发过程中,如何保证代码的质量,降低后期维护的成本,以及为项目越来越快的版本迭代速度提供支持,成为了我们关注的重要问题。这篇文章将阐明我们在猿题库 iOS 客户端的架构设计。MVCMVC,Model-View-Controller,我们从这个古老而经典的设计模式入手。采用 MVC 这个架构的最大的优点在于其概念简单,易于理解,几乎任何一个程序员都会有所了解,几乎每一所计算机院校都教过相关的知识。而在 iOS 客户端开发中,MVC 作为官方推荐的主流架构,不但 SDK 已经为我们实现好了 UIView、UIViewController 等相关的组件,更是有大量的文档和范例供我们参考学习,可以说是一种非常通用而成熟的架构设计。但 MVC 也有他的坏处。由于 MVC 的概念过于简单朴素,已经越来越难以适应如今客户端的需求,大量的代码逻辑在 MVC 中并没有定义得很清楚究竟应该放在什么地方,导致他们很容易就会堆积在 Controller 里,成为了人们所说的 Massive View Controller。MVVMMVVM,Model-View-ViewModel,一个从 MVC 模式中进化而来的设计模式,最早于2005年被微软的 WPF 和 Silverlight 的架构师 John Gossman 提出。在 iOS 开发中实践 MVVM 的话,通常会把大量原来放在 ViewController 里的视图逻辑和数据逻辑移到 ViewModel 里,从而有效的减轻了 ViewController 的负担。另外通过分离出来的 ViewModel 获得了更好的测试性,我们可以针对 ViewModel 来测试,解决了界面元素难于测试的问题。MVVM 通常还会和一个强大的绑定机制一同工作,一旦 ViewModel 所对应的 Model 发生变化时,ViewModel 的属性也会发生变化,而相对应的 View 也随即产生变化。同样的,MVVM 也有他的缺点:一个首要的缺点是,MVVM 的学习成本和开发成本都很高。MVVM 是一个年轻的设计模式,大多数人对他的了解都不如 MVC 熟悉,基于绑定机制来进行编程需要一定的学习才能较好的上手。同时在 iOS 客户端开发中,并没有现成的绑定机制可以使用,要么使用 KVO,要么引入类似 ReactiveCocoa 这样的第三方库,使得学习成本和开发成本进一步提高。另一个缺点是,数据绑定使 Debug 变得更难了。数据绑定使程序异常能快速的传递到其他位置,在界面上发现的 Bug 有可能是由 ViewModel 造成的,也有可能是由 Model 层造成的,传递链越长,对 Bug 的定位就越困难。同时还必须指出的是,在传统的 MVVM 架构中,ViewModel 依然承载的大量的逻辑,包括业务逻辑,界面逻辑,数据存储和网络相关,使得 ViewModel 仍然有可能变得和 MVC 中 ViewController 一样臃肿。在两种架构中权衡而产生的架构两种架构的优点都想要,缺点又都想避开,我们在两种架构中权衡了他们的优缺点,设计出了一个新的架构,起了一个名字叫:MVVM without Binding with DataController,架构图如下:
ViewModel先来看右边视图相关的部分,传统的 MVC 当中 ViewController 中有大量的数据展示和样式定制的逻辑,我们引入 MVVM 中 ViewModel 的概念,将这部分视图逻辑移到了 ViewModel 当中。在这个设计中,每一个 View 都会有一个对应的 ViewModel,其包含了这个 View 数据展示和样式定制所需要的所有数据。同时,我们不引入双向绑定机制或者观察机制,而是通过传统的代理回调或是通知来将 UI 事件传递给外界。而 ViewController 只需要生成一个 ViewModel 并把这个装配给对应的 View,并接受相应的 UI 事件即可。这样做有几个好处:首先是 View 的完全解耦合,对于 View 来说,只需要确定好相应的 ViewModel 和 UI 事件的回调接口即可与 Model 层完全隔离;而 ViewController 可以避免与 View 的具体表现打交道,这部分职责被转交给了 ViewModel,有效的减轻了 ViewController 的负担;同时我们弃用了传统绑定机制,使用了传统的易于理解的回调机制来传递 UI 事件,降低了学习成本,同时使得数据的流入和流出变得易于观察和控制,降低了维护了调适的成本。DataController接下来我们关注 Model 和 VC 之间的关系。如之前提到,在传统的 MVVM 中,ViewModel 接管了 ViewController 的大部分职责,包括数据获取,处理,加工等等,导致其很有可能变得臃肿。我们将这部分逻辑抽离出来,引入一个新的部件,DataController。ViewController 可以向 DataController 请求获取或是操作数据,也可以将一些事件传递给 DataController,这些事件可以是 UI 事件触发的。DataController 在收到这些请求后,再向 Model 层获取或是更新数据,最后再将得到的数据加工成 ViewController 最终需要的数据返回。这样做之后,使得数据相关的逻辑解耦合,数据的获取、修改、加工都放在 Data Controller 中处理,View Controller 不关心数据如何获得,如何处理,Data Controller 也不关心界面如何展示,如何交互。同时 Data Controller 因为完全和界面无关,所以可以有更好的测试性和复用性。DataController 层和 Model 层之间的界限并不是僵硬的,但需要保证每一个 ViewController 都有一个对应的 DataController。Data Controller 更强调的是其作为业务逻辑对外的接口。而在 DataController 中调用更底层的 Model 层逻辑是我们推荐的编程范式,例如数据加工层,网络层,持久层等。在后面的例子中,我们会更详细的讲解 DataController 的实现细节。Show me the code我们以猿题库主页为例,展示我们是如何使用应用这个架构的。
主页有几个部分组成,最上面的小猴子 Banner 页,用于滚动展示一些活动信息;中间有一个用户名字的页面,用于展示用户信息和答题情况以及一些心灵鸡汤;最底下的这部分是一个课目选择页面,展示了用户开启的科目入口,在更多选项里面可以进一步配置这些科目入口。接下来我们会以科目页面(SubjectView)为例展示一些细节。ViewController我们会给每一个 ViewController 都创建一个对应的 DataController。 例如我们给主页建一个类起名叫APEHomePraticeViewController,同时他会有一个对应的 DataController 起名叫APEHomePraticeDataController。同时我们把页面拆分为几个部分,每个部分有一个相对应的 SubView。代码如下:
@interface APEHomePracticeViewController ()
@property (nonatomic, strong, nullable) UIScrollView *contentV
@property (nonatomic, strong, nullable) APEHomePracticeBannerView *bannerV
@property (nonatomic, strong, nullable) APEHomePracticeActivityView *activityV
@property (nonatomic, strong, nullable) APEHomePracticeSubjectsView *subjectsV
@property (nonatomic, strong, nullable) APEHomePracticeDataController *dataC
在viewDidLoad的时候,初始化好各个 SubView,并设置好布局:
- (void)setupContentView {
self.contentView = [[UIScrollView alloc] init];
[self.view addSubview:self.contentView];
self.bannerView = [[APEHomePracticeBannerView alloc] init];
self.activityView = [[APEHomePracticeActivityView alloc] init];
self.subjectsView = [[APEHomePracticeSubjectsView alloc] init];
self.subjectsView.delegate =
[self.contentView addSubview:self.bannerView];
[self.contentView addSubview:self.activityView];
[self.contentView addSubview:self.subjectsView];
// Layout Views ...
接下来,ViewController 会向 DataController 请求 Subject 相关的数据,并在请求完成后,用获得的数据生成 ViewModel,将其装配给 SubjectView,完成界面渲染,代码如下:
- (void)fetchSubjectData {
[self.dataController requestSubjectDataWithCallback:^(NSError *error) {
if(error == nil) {
[self renderSubjectView];
- (void)renderSubjectView {
APEHomePracticeSubjectsViewModel *viewModel =
[APEHomePracticeSubjectsViewModel viewModelWithSubjects:self.dataController.openSubjects];
[self.subjectsView bindDataWithViewModel:viewModel];
数据结构为了更好的演示,我们接下来要介绍一下 Subject 相关的数据结构:APESubject是科目的资源结构,包含了 Subject 的 id 和 name 等资源属性,这部分属性是用户无关的;APEUserSubject是用户的科目信息,包含了用户是否打开某个学科的属性。
@interface APESubject : NSObject
@property (nonatomic, strong, nullable) NSNumber *
@property (nonatomic, strong, nullable) NSString *
@interface APEUserSubject : NSObject
@property (nonatomic, strong, nullable) NSNumber *
@property (nonatomic, strong, nullable) NSNumber *updatedT
@property (nonatomic) APEUserSubjectS
DataController如我们之前所说,每一个 ViewController 都会有一个对应的 DataController,这一类 DataController 的主要职责是处理这个页面上的所有数据相关的逻辑,我们称其为 View Related Data Controller。
// APEHomePracticeDataController.h
@interface APEHomePracticeDataController : APEBaseDataController
@property (nonatomic, strong, nonnull, readonly) NSArray *openS
- (void)requestSubjectDataWithCallback:(nonnull APECompletionCallback)
上面的这个代码我们定义了一个界面最终需要的数据的 property,这里是openSubjects,这个 property 会存储用户打开的科目列表,他的类型是APESubject。我们还会定义一个接口来请求 openSubject 数据。DataController 这一层是一个灵活性很高的部件,一个 DataController 可以复用更小的 DataController,这一类更小的 DataController 通常只会包含纯粹的或是更抽象的 Model 相关的逻辑,例如网络请求,数据库请求,或是数据加工等。我们称这一类 DataController 为 Model Related Data Controller。Model Related Data Controller 通常会为上层提供正交的数据:
// APEHomePracticeDataController.m
@interface APEHomePracticeDataController ()
@property (nonatomic, strong, nonnull) APESubjectDataController *subjectDataC
@implementation APEHomePracticeDataController
- (void)requestSubjectDataWithCallback:(nonnull APECompletionCallback)callback {
APEDataCallback dataCallback = ^(NSError *error, id data) {
callback(error);
[self.subjectDataController requestAllSubjectsWithCallback:dataCallback];
[self.subjectDataController requestUserSubjectsWithCallback:dataCallback];
- (nonnull NSArray *)openSubjects {
returnself.subjectDataController.openSubjectsWithCurrentPhase ?: @[];
在我们的APEHomePraticeDataController的实现中,就包含了一个APESubjectDataController,这个subjectDataController会负责请求 All Subjects 和 User Subjects,并将其加工成上层所最终需要的 Open Subjects。(备注:这个例子里面的 callback 会回调多次是猿题库产品的需求,如有需要,可在这一层控制请求都完成后再调用上层回调)事实上,Model Related Data Controller 可以一般性的认为就是大家经常在写的 Model 层代码,例如 UserAgent,UserService,PostService 之类的服务。之后读者若想重构就项目成这个架构,大可以不必纠结于形式,直接在 DataController 里调用旧有代码的逻辑即可,如图下面这样的行为都是允许的:
ViewModel每一个 View 都会有一个对应的 ViewModel,这个 ViewModel 会包含展示这个 View 所需要的所有数据。我们会使用工厂方法来创建 View Model,例如这个例子里,Subject View Model 不需要关心传递给他是什么样的 Subject,所有的课目或者只是用户开启的科目。
@interface APEHomePracticeSubjectsViewModel : NSObject
@property (nonatomic, strong, nonnull) NSArray*cellViewM
@property (nonatomic, strong, nonnull) UIColor *backgroundC
+ (nonnull APEHomePracticeSubjectsViewModel *)viewModelWithSubjects:(nonnull NSArray *)
ViewModel 可以包含更小的 ViewModel,就像 View 可以有 SubView 一样。SubjectView 的内部是由一个UICollectionView实现的,所以我们也给了对应的 Cell 设计了一个 ViewModel。需要额外注意的是,ViewModel 一般来说会包含的显示界面所需要的所有元素,但粒度是可以控制。一般来说,我们只把会因为业务变化而变化的部分设为 ViewModel 的一部分,例如这里的 titleColor 和 backgroundColor 会因为主题不同而变化,但字体的大小(titleFont)却是不会变的,所以不需要事无巨细的都加到 ViewModel 里。
@interface APEHomePracticeSubjectsCollectionCellViewModel : NSObject
@property (nonatomic, strong, nonnull) UIImage *
@property (nonatomic, strong, nonnull) UIImage *highlightedI
@property (nonatomic, strong, nonnull) NSString *
@property (nonatomic, strong, nonnull) UIColor *titleC
@property (nonatomic, strong, nonnull) UIColor *backgroundC
+ (nonnull APEHomePracticeSubjectsCollectionCellViewModel *)viewModelWithSubject:(nonnull
APESubject *)
+ (nonnull APEHomePracticeSubjectsCollectionCellViewModel *)viewModelForM
ViewView 只需要定义好装配 ViewModel 的接口和定义好 UI 回调事件即可:
@protocol APEHomePracticeSubjectsViewDelegate - (void)homePracticeSubjectsView:(nonnull APEHomePracticeSubjectsView *)subjectView
didPressItemAtIndex:(NSInteger)
@interface APEHomePracticeSubjectsView : UIView
@property (nonatomic, strong, nullable, readonly) APEHomePracticeSubjectsViewModel *viewM
@property (nonatomic, weak, nullable)
- (void)bindDataWithViewModel:(nonnull APEHomePracticeSubjectsViewModel *)viewM
渲染界面的时候,完全依靠 ViewModel 进行,包括 View 的 SubView 也会使用 ViewModel 里面的子 ViewModel 渲染。
- (void)bindDataWithViewModel:(nonnull APEHomePracticeSubjectsViewModel *)viewModel {
self.viewModel = viewM
self.backgroundColor = viewModel.backgroundC
[self.collectionView reloadData];
[self setNeedsUpdateConstraints];
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:
(NSIndexPath *)indexPath {
APEHomePracticeSubjectsCollectionViewCell *cell = [collectionView
dequeueReusableCellWithReuseIdentifier:@"Cell"forIndexPath:indexPath];
if(0 &= indexPath.row && indexPath.row & self.viewModel.cellViewModels.count) {
APEHomePracticeSubjectsCollectionCellViewModel *vm =
self.viewModel.cellViewModels[indexPath.row];
[cell bindDataWithViewModel:vm];
至此,我们就完成了所有的步骤。我们回过头再看一下 ViewController 的职责就回变的非常简单,装配好 View,向 DataController 请求数据,装配 ViewModel,配置给 View,接收 View 的UI事,一切复杂的操作都能够的代理出去。总结优点通过上面的例子我们可以看到,这个架构有几个优点:层次清晰,职责明确:和界面有关的逻辑完全划到 ViewModel 和 View 一遍,其中 ViewModel 负责界面相关逻辑,View 负责绘制;Data Controller 负责页面相关的数据逻辑,而 Model 还是负责纯粹的数据层逻辑。 ViewController 仅仅只是充当简单的胶水作用。耦合度低,测试性高:除开 ViewController 外,各个部件可以说是完全解耦合的,各个部分也是可以完全独立测试的。同一个功能,可以分别由不同的开发人员分别进行开发界面和逻辑,只需要确立好接口即可。复用性高:解耦合带来的额外好处就是复用性高,例如同一个View,只需要多一个工厂方法生成 ViewModel,就可以直接复用。数据逻辑代码不放在 ViewController 层也可以更方便的复用。学习成本低: 本质上来说,这个架构属于对 MVC 的优化,主要在于解决 Massive View Controller 问题,把原本属于 View Controller 的职责根据界面和逻辑部分相应的拆到 ViewModel 和 DataController 当中,所以是一个非常易于理解的架构设计,即使是新手也可以很快上手。开发成本低: 完全不需要引入任何第三方库就可以进行开发,也避免了因为 MVVM 维护成本高的问题。实施性高,重构成本低:可以在 MVC 架构上逐步重构的架构,不需要整体重写,是一种和 MVC 兼容的设计。缺点不可否认的是,这个设计也有其相应的缺点,由于其把传统 MVVM 里面的 VM 拆成两部分,会照成下面的一些情况:当页面的交互逻辑非常多时,需要频繁的在 DC-VC-VM 里来回传递信息,造成了大量胶水代码。另外,由于在传统的 MVVM 中 VM 原本是一体的,一些复杂的交互本来可以在 VM 中直接完成测试,如今却需要同时使用 DC 和 VM 并附上一些胶水代码才能进行测试。没有了 Binding,代码写起来会更费劲一点(仁者见仁,智者见智)。后记MVVM 是一个很棒的架构,私底下我也会用其来做一些个人项目,但在公司项目里,我会更慎重的考虑个中利弊。我做这个设计的时候,心仪 MVVM 的种种好处,又忌惮于它的种种坏处,再考虑到团队的开发和维护成本,所以最终设计成了如今这样。个人认为,好的架构设计的都是和团队以及业务场景息息相关的。我们这套架构帮助我们解决了 ViewController 代码堆积的问题,也带来了更清晰明了的代码层级和模块职责,同时没有引入过多的复杂性。希望大家也能充分理解这套架构的适用场景,在自己的 APP 架构设计中有所借鉴。
iOS开发,爱折腾,爱挑战,爱生活
Lancy's Blog Blog Archives About MeTwitterWeiboGitHubRSS 猿题库 iOS 客户端架构设计 序 猿题库是一个拥有数千万用户的创业公司,从2013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个...
原文: iOS应用架构谈 view层的组织和调用方案 iOS应用架构谈 开篇 iOS应用架构谈 网络层设计方案 iOS应用架构谈 本地持久化方案及动态部署 iOS应用架构谈 组件化方案 前言《iOS应用架构谈 开篇》出来之后,很多人来催我赶紧出第二篇。这一篇文章出得相当艰难...
转自http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html 前言《iOS应用架构谈 开篇》出来之后,很多人来催我赶紧出第二篇。这一篇文章出得相当艰难,因...
引言:2016,开启了人工智能(AI)元年,在世界范围内掀起了一轮新的技术和应用革命。由于云计算、大数据、机器学习、深度学习等技术的不断发展进步,伴随着人工红利消失、消费升级、个性化用户体验的刚性需求催化了各种AI应用的快速落地,包括无人驾驶、计算机视觉、智能聊天机器人等人...
日如是家人黄愈惠,种种子第57天。 发心:我今不是为了我个人闻思修行,而是为了六道轮回一切如母有情众生,愿一切如母有情众生能够早日离苦得乐,清净业障,福慧增长,早证菩提! 往昔所造诸恶业, 皆由无始贪嗔痴; 从身语意之所生; 一切我今皆忏悔。 (1)临摹《...
save stash$ git stash list stash$ git stash list apply stash#apply the latest stash$ git stash apply#apply older stash$ git stash apply s...
相亲,在过年期间应该是一个很热门的话题,要么被迫,要么无奈,要么必须自愿。看过了太多的相亲,也听过很多的版本,也被小说中各种奇葩相亲场景给逗乐的不行,但今天,确实自己也亲自体验了一次相与被相的经历。 下午刚跟老妈聊完自己的婚姻观,接着老爸就说:A叔叔带着H君来了。你们肯定也...
关于改变要讨论三部分:骑马人,马,环境。(书里说的是大象,我总觉得怪怪的,改用自己喜欢的隐喻) 每个人都是人马座,半人半兽。都有人的理智,逻辑,按部就班的执行力(L型思维模式,线性模式,理性思维);也有马那种动物性需要引导控制加以利用。欢脱,本能,有能量(R型的非线性搜索引...}

我要回帖

更多关于 苹果手机屏幕摔碎了 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信