iOS 应用架构现状分析

淡蓝的枫 8年前
   <p>iOS从2007年诞生至今已有近10年的历史,10年的时间对iOS技术圈来说足够产生相当可观的沉淀,尤其这几年的技术分享氛围无论国内国外都显得异常活跃。本文就iOS架构这一主题,结合开发圈里讨论较多的几种主流方式,再配以博主自己的理解,做下现状分析。给自己做下知识梳理的同时,也期望能引入新的思考。</p>    <h3><strong>架构的定义</strong></h3>    <p>过去6年多几乎绝大部分时间都浸淫在iOS平台,翻阅过不少关于架构的文章,发现众人对架构的理解颇有些差异,总体来说可分为四类:</p>    <p><strong>第一类:精简型应用架构</strong></p>    <p>这类架构的文章分析主要还是围绕MVC展开,以苹果自带UIViewController优劣为出发点,再结合主流的MVP,MVVM,MVCS等变种进行分析演变。这类的探讨重点在于M,V,C三类角色的定义以及之间的数据事件流向的规范。很多小型应用所面临的问题及其架构层面的解决方案都集中在这一类。</p>    <p><strong>第二类:综合型应用架构</strong></p>    <p>对于用户量级在千万级或以上的应用来说,MVC这一层面的思考已无法应对业务疯狂增长所带来的负担。这类应用往往需要专业资深的架构师出面进行深层次的思考设计,业内不少大厂如淘宝,天猫,携程等都做过一些分享。不过到了这一层级的战斗,不光考验架构师的技术积累,更重要的是架构师对于业务的整体理解。我姑且把这类架构名之为:综合型应用架构。综合型应用架构一般不会提到MVC,更多是在探讨“层”与“模块”的划分和耦合。后面我会就几个经典样本做下详尽深入的分析。</p>    <p><strong>第三类:深度优化的综合型应用架构</strong></p>    <p>综合型应用架构是应对大规模业务增长的必经之路,一旦架构成型,后期业务膨胀会不停的打磨架构本身,产品本身对体验质量的追求会要求架构师和技术团队不停的优化架构细节。这种优化可以分为两块,第一是组件或模块划分的粒度越来越细,第二是组件模块的深度优化,比如网络层的深度优化,sqlite优化(多线程,FTS,安全等),数据加密,HotFix,Hybrid等,一些开源的第三方库已不能满足要求,需要团队自己重造轮子。这一层面的架构设计涉及面广,对架构师,团队技术人员的技术深度和业务理解能力有较高要求,短短一篇技术文章往往只能走马观花的介绍个大概,每一次优化几乎都可以作为一个专题来讲解。</p>    <p>第四类:组织型应用架构</p>    <p>这类架构在第三类的基础之上更进了一步,除了关注系统层面的架构设计之外,更对团队或部门之间协作方式,各系统模块的演进方式,产品发布流程等都做了规范。除去业务膨胀带来的压力,人员增长,各团队协作依赖增强等都会对app的质量,迭代速度产生影响,这些问题也需要从架构层面去解决。这类结合技术架构和组织架构的分享还比较少。</p>    <p>以上四种类型的架构又可以看做一般App从简至繁,公司规模随之增长的演进过程,技术圈绝大部分的架构类分享文章都可以归为上述四类。</p>    <p>对于什么是架构的学术定义,似乎大家并不太在意,更关心的是如何解决自身项目当下的问题。虽然在我看来第一类架构更像是在讨论设计模式,但这里面确实又有非常多的知识可以深入挖掘,这里就把所有“解决应用整体设计问题”的讨论都归类于架构这一话题。</p>    <p>值得一提的是,架构师的视野和积累一般都受限于自己所经历项目及业务的规模。如果有机会,工程师还是应该尽可能去BAT这类巨头级公司历练一下,知识深度和广度的构建绝非纸上可得。</p>    <h3><strong>样本分析</strong></h3>    <p>这里我以圈子里几个经典的架构分享为样本,做下解剖分析。样本的选取主要以搜索引擎及评论热度为标准,排名不分先后。</p>    <p><strong>第一类样本:</strong></p>    <p>如果一篇架构的文章是以探讨MVC为起点,很有可能作者所说的架构就可以被归为第一类样本。</p>    <p>Xcode自带的UIViewController模板经常被戏称为“Massive View Controller”,主要是因为UIViewController除了负责Controller生命周期回调之外,还承担了View和Controller的工作。不少架构的探讨以此为基础,将Controller的M,V,C三个角色重新进行划分,又或者衍生出MVP,MVVM,MVCS,VIPER等其他组织方式。重新定义MVC之后,网络访问,持久化,安全等通用功能模块往往就由Controller或者Presenter负责了,这些负责业务逻辑的功能类直接依赖于成熟稳定的第三方基础库,比如AFNetworking,FMDB等。</p>    <p>但是,应付过复杂庞大业务模块的工程师会会有经验,无论以何种方式组织Controller,数据流向和事件传递再清晰合理,单纯代码量的膨胀就足以让Controller变得难以维护。MVC,MVP,MVVM都可以变得Massive。想象一下将10本书放在床头,你可以按翻阅频次,阅读习惯将书合理摆放,但当你面对100本书的时候,已不是如何摆放的问题,而是需要一个新的书柜。说到这里我挺服VIPER的,五个角色划分,尝试可以在床头放下100本书的Pattern。我们先来看看“10本书”的样本。</p>    <p>样本名:iOS 架构模式–解密 MVC,MVP,MVVM以及VIPER架构</p>    <p>英文原版,中文翻译版</p>    <p>作者:Bohdan Orlov</p>    <p>这篇文章是第一类样本的典型,综合对比了MV(X)系列架构。我们来看下主要论点。</p>    <p><strong>论点摘要:</strong></p>    <p>文章认为优秀的架构具备以下三个特质:</p>    <p>Balanced distribution of responsibilities among entities with strict roles.</p>    <p>能把代码职责均衡的划分到不同的功能类里。</p>    <p>这是我们关注架构的原动力,有个粗浅的评判标准,同一个业务单位(Controller)里面,不能一个类1000多行代码,另一个类却只有10来行。当我们考虑网络请求的代码是该放在controller当中还是model当中的时候,在“代码量”上要做到“雨露均沾”,不能少数类“营养不良”。所以不论你的MVC,MVP或者VIPER是如何划分职责的,各个角色所承担的职责应当看起来是均匀分配的。</p>    <p>Testability usually comes from the first feature.</p>    <p>方便测试</p>    <p>测试是个有趣的话题,不同公司实践差异很大。我知道有些团队的产品质量严重依赖于QA团队,有些却干脆没有测试团队,完全依赖于开发人员自己保证质量,这两种风格一般取决于CTO的技术决策,和公司大小倒没多少关系。对于开发人员自测的情况,如果代码架构清晰,各角色职责分配合理,易测性是随之而来的副产品。</p>    <p>Ease of use and a low maintenance cost</p>    <p>易用且方便维护</p>    <p>易用是针对团队里的开发人员而言的。其实到底是用MVP还是MVVM,或者MVVM里数据如何流动,这些具体的规范到底采用何种形态不是最重要的问题,关键是在团队所有成员能有一致的认识,无论是写代码还是Debug都能快速切入。</p>    <p>围绕上面三个特质,作者一一分析对比了传统MVC,Apple MVC,MVP,MVVM,VIPER在这三方面的表现,结论是到底选用哪个纯粹是个人口味问题。相较于结论,作者对各个Pattern的角色定义及分析才是真正有价值的部分,我们在阅读评断其他类似架构文章的时候,也应该重在了解作者对于角色的理解。大家可以基于这一点,阅读下其他一些类似的文章。VIPER理论上并不能归类于MV(X)系列,而是突破了MVC的思考方式,比如定义了Router处理页面流程,这对架构师是个很好的思路,在对MV(X)重新设计的时候也应该能有新的思考,所谓“君子不器”。</p>    <p>MV(X)都是以MVC为原型进行变化,MV(X)到底如何使用没有教科书式的标准答案,关键在于架构师和团队各成员能达成一致的认识,在遇到问题时能不断的做调整重构,遇到难以跨越的瓶颈时,能跳出MV(X)寻找解决方案。</p>    <p><strong>第二类样本:</strong></p>    <p>在开始分析第二类样本之前,有几个概念比较重要,是我们深入讨论各个综合型样本的前提。组件,模块,层的定义。</p>    <p>在我看来,组件是比模块更小的功能单位,不具备业务属性,只处理基础通用的问题,类似于工具箱。比如我们给NSString写的Category提供base64,给NSDate写的Category做日期格式化等等。</p>    <p>模块较之组件粒度更大一些,另外最重要的区别是带有业务属性,和业务场景相关联。比如购物车模块,注册登录模块,支付模块等等,模块往往会对一些通用的组件产生依赖。</p>    <p>层是另一个维度的概念了,我平常阅读技术文章的时候,发现很多人会把模块和层的概念混淆,可以用一张图来区分模块与层的概念:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/13a5e97d601851a59699400bb6d01a43.png"></p>    <p>对于层不允许跨层访问,也就是说A不能直接访问C,下层不允许访问上层,B无法知道A的实现细节。层的概念可类比TCP/IP协议栈。模块就灵活多了,A,B,C三者之间可以任意访问依赖。所以一般来说层对架构的约束更高,但使得依赖更规范好维护,模块在复杂的场景下很容易结成一个网状的依赖形态,难以维护。第二类项目架构都是围绕层与模块的划分所展开,有的有严格的层次结构,有的不分层次,通过接口严格规范各模块交互,有的二者结合,在某一层内再做模块划分,下面我们分析的样本都属于上面三种情形。</p>    <p>在开始分析之前,建议大家全篇了解下iOS开发圈关于组件化的讨论,我之前做过一个总结,里面有各篇文章链接:http://mrpeak.cn/blog/module/ 。</p>    <p>样本名:饿了么移动APP的架构演进</p>    <p>原文地址</p>    <p>作者:圣迪</p>    <p>这篇架构演进的讲解是典型的从第一类到第二类的过渡,可简化为以下几步:</p>    <p>1.使用传统MVC快速迭代App。</p>    <p>2.业务发展,有多个App需要开发,开始组件化,使用CocoaPods进行组件管理,目标是并行开发和代码重用。架构图如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/df462884317599b69f2155303a984d06.jpg"></p>    <p>如果读过上面关于组件化的文章,这张架构图就很好理解了,在做好模块划分和管理之后,通过Router的方式将让各模块产生耦合。这种架构方式已初步具备一个应付大规模业务增长的架构模型。</p>    <p>3.Hybrid,几乎所有运营类主导的App都会最终投入Hybrid的怀抱,运营团队对快速上线的要求只能在H5或者Hybrid上得以实现,不过这和架构本身关系不太大,是另一个大的话题。</p>    <p>4.React-Native & Hot Patch,这和第三步类似都是由运营驱动,几乎是所有内容运营型app的必经之路。</p>    <p>这四步的演化文章介绍的还比较粗略,更多的细节(比如Router内部实现,业务组件和非业务组件的分工,组件间耦合的方式等)没有介绍,或者饿了么App端也还在探索当中。</p>    <p><strong>第三类样本:</strong></p>    <p>样本名:携程移动APP架构优化之旅</p>    <p>原文地址</p>    <p>作者:陈浩然</p>    <p>携程App端的架构演化在饿了么架构基础之上,多了很多优化的细节。</p>    <p>提到了对于基础SDK组件和业务组件的具体划分:</p>    <p>核心功能SDK化</p>    <p>• 通讯、定位、Hybrid、数据库、登录、分享、基础库等</p>    <p>• 直接提供给其他BU独立App使用</p>    <p>公用业务功能组件化</p>    <p>• 地图、日历、城市、图片、通讯录等13个公共组件</p>    <p>• 减少各BU重复开发工作量</p>    <p>性能数据指标采集:</p>    <p>• 网络性能:网络服务成功率、平均耗时、耗时分布</p>    <p>• 定位:获取经纬度成功率、城市定位成功率</p>    <p>• 启动时间、内存、流量等指标</p>    <p>• 多种纬度:系统、App版本、网络状况、位置等</p>    <p>网络优化</p>    <p>• 使用TCP长连接实现网络服务</p>    <p>• 根据网络状况2G/3G/4G/WIFI进行调优参数</p>    <p>• 根据连接/读/写不同阶段使用重试机制</p>    <p>• 使用IP列表避免DNS解析失败或者劫持</p>    <p>• 根据网络延迟选择服务端IP(使用Ping)</p>    <p>• 使用ProtocolBuffer+Gzip减少Payload</p>    <p>还有Hybrid,HotFix,React Native等就不一一列举了,这些细节性强的分享有很高的学习价值,能对刚涉足某一块优化的架构师起到方向指引的作用。</p>    <p>这些看似简单的划分往往很考验架构师的大局观和经验,涵盖面和合理的粒度掌控才能让技术团队的开发工作高效并行。</p>    <p><strong>第四类样本:</strong></p>    <p>样本名:非死book iOS Architecture</p>    <p>视频地址1 视频地址2</p>    <p>作者:非死book</p>    <p>除了和上面样本类似的模块化,组件优化的介绍之外,还有两方面新的信息:</p>    <p>在关于Infrastructure的介绍当中有张示意图介绍团队分工:</p>    <p><img src="https://simg.open-open.com/show/c50453ee9cad33e51be5a507c5642d77.jpg"></p>    <p>视频当中不光介绍了基础组件,业务模块的划分,和说明了这些划分对应的团队是如何进行协作。</p>    <ul>     <li> <p>最底层的是基础设施团队,负责基础SDK的开发,可以对应之前谈到的非业务组件。</p> </li>     <li> <p>中间层是业务团队,按业务模块进行划分,一个业务对应一个团队,团队负责全平台的开发,业务团在开发过程当中可反过来回馈基础SDK。</p> </li>     <li> <p>最上面是产品的发布团队,产品发布团队配合业务团队对产品的发布周期和质量做保障。</p> </li>    </ul>    <p>在非死book iOS客户端架构设计当中有关于model层介绍:</p>    <p>之前看过不少架构分享文章,少有对model层深入探讨的,一般提到都是一笔带过。model层涉及app数据持久化,以及runtime的状态维护,对app的稳定性和迭代效率起到至关重要的影响。</p>    <p>非死book的model layer采用的是immutable策略,model虽然能被各层访问,但model的修改统一由model layer提供接口。上层业务团队一旦想修改某个model property,需要向model layer维护团队提出申请,这一设计对状态的维护相当谨慎。</p>    <h3><strong>我的思考</strong></h3>    <p>将架构的类型归为上述四类也是方便自己搭建关于架构的知识体系,看架构类文章的时候先做好分类再按类别查漏补缺汲取营养。我个人在第一类,第二类架构方面做过一些尝试和积累,第三类优化上做过一些知识总结。</p>    <p>第一类比如之前的技术分享:</p>    <p>iOS的应用层CDD架构http://mrpeak.cn/blog/cdd/</p>    <p>这种优化应用层的架构方式是在MV(X)系列基础之上新的尝试,已在公司项目当中有过实践,成效尚可。</p>    <p>第二类比如:</p>    <p>用Swift搭建数据驱动型iOS架构http://mrpeak.cn/blog/swift-dda/</p>    <p>搭建数据驱动型Android架构http://mrpeak.cn/blog/dda-android/</p>    <p>用了Swift和Java实现相同的架构思路,这种架构方式也是从已有成熟的项目当中总结提炼而来。</p>    <p>第三类主要是优化和总结的文章:</p>    <p>App安全之网络传输安全</p>    <p>HTTP 2.0的那些事</p>    <p>iOS组件化方案</p>    <p>iOS网络请求优化之DNS映射</p>    <p>关于架构的总结就到这里,以上全是一家之言,一则梳理,二则分享,任何想法建议,欢迎交流。</p>    <p> </p>    <p> </p>    <p> </p>    <p>来自:http://mp.weixin.qq.com/s?__biz=MzI5MjEzNzA1MA==&mid=2650264144&idx=1&sn=97d62594cebfa0fac54feac52c024031&chksm=f4068365c3710a73a470305724d841028cc09f92adc665d46760cacbe2eb1a7eecae78dc08f2#rd</p>    <p> </p>