iOS干货

浅谈iOS应用架构

2007年1月9日苹果公司在Macworld展览会上公布,随后于同年的6月发布第一版iOS操作系统,最初的名称为“iPhone Runs OS X”。至今已经10年的历史了。我接触iOS也有快5年了,从刚上手到现在,做了几十个App,我想说,技术这块真的不在多,在精。

我这篇文章可能不会谈太多的干货,应该会多说一些我的个人经验体会吧。所以如果你是技术大牛,就请别在我这浪费时间了。

在过去的几年里,我做的最快的一个外包项目,是一个叫智慧农业的新闻类项目,从0到项目提交,花了15天时间不到吧。交互体验我觉得吧。。。嘿嘿,因为当时发包方没有过多要求,只是说仿照腾讯的首页,并且我也没有UI什么的,一切靠自己了,幸好我当时有小伙伴,后台PHP一个,Android一个,最后,这个项目很愉快的交接了。我记得每人当时分了1万多,具体记不清了。我这人理财不好,要改。这个项目做得快,(当然是我自认为的哈),于是就造成了一些弊端,完全没考虑架构。。后来再看自己写的代码,简直不忍直视。现在反思一下,我除了赚了点外快,我的技术有长进么?答案是没有。当然这是对于我这一类的人来说。反而,如果花接外包的这种热情、精力、时间去深挖一下技术,我想,我会有很大长进。ok,我又扯远了。

  架构(Software architecture)

“架构,又名软件架构是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。架构描述语言(ADL)用于描述软件的体系架构。现在已有多种架构描述语言,如Wright(由卡内基梅隆大学开发),Acme(由卡内基梅隆大学开发),C2(由UCI开发),Darwin(由伦敦帝国学院开发)。ADL的基本构成包括组件、连接器和配置。”

  架构历史  (粗略了解下就ok)

  早在1960年代,诸如E·W·戴克斯特拉就已经涉及软件架构这个概念了。自1990年代以来,部分由于在 Rational Software Corporation 和Microsoft内部的相关活动,软件架构这个概念开始越来越流行起来。
卡内基梅隆大学和加州大学埃尔文分校在这个领域作了很多研究。卡内基·梅隆大学的Mary Shaw和David Garlan于1996年写了一本叫做 Software Architecture perspective on an emerging discipline的书,提出了软件架构中的很多概念,例如软件组件、连接器、风格等等。 加州大学埃尔文分校的软件研究院所做的工作则主要集中于架构风格、架构描述语言以及动态架构。
计算机软件的历史开始于五十年代,历史非常短暂,而相比之下建筑工程则从石器时代就开始了,人类在几千年的建筑设计实践中积累了大量的经验和教训。建筑设计基本上包含两点,一是建筑风格,二是建筑模式。独特的建筑风格和恰当选择的建筑模式,可以使它成为一个独一无二的建筑。
下面的照片显示了中美洲古代玛雅建筑,Chichen-Itza大金字塔,九个巨大的石级堆垒而上,九十一级台阶(象征着四季的天数)夺路而出,塔顶的神殿耸入云天。所有的数字都如日历般严谨,风格雄浑。难以想象这是石器时代的建筑物。
  软件与人类的关系是架构师必须面对的核心问题,也是自从软件进入历史舞台之后就出现的问题。与此类似地,自从有了建筑以来,建筑与人类的关系就一直是建筑设计师必须面对的核心问题。英国首相丘吉尔说,我们构造建筑物,然后建筑物构造我们(We shape our buildings, and afterwards our buildings shape us)。英国下议院的会议厅较狭窄,无法使所有的下议院议员面向同一个方向入座,而必须分成两侧入座。丘吉尔认为,议员们入座的时候自然会选择与自己政见相同的人同时入座,而这就是英国政党制的起源。Party这个词的原意就是”方”、”面”。政党起源的关键就是建筑物对人的影响。
  在软件设计界曾经有很多人认为功能是最为重要的,形式必须服从功能。与此类似地,在建筑学界,现代主义建筑流派的开创人之一Louis Sullivan也认为形式应当服从于功能(Forms follows function)。
  几乎所有的软件设计理念都可以在浩如烟海的建筑学历史中找到更为遥远的历史回响。最为著名的,当然就是模式理论和XP理论。

  设计目标  (这个应该很好记)

正如同软件本身有其要达到的目标,软件架构设计要达到如下的目标:
1.可靠性(Reliable)。软件系统对于用户的商业经营和管理来说极为重要,因此软件系统必须非常可靠。
2.安全性(Secure)。软件系统所承担的交易的商业价值极高,系统的安全性非常重要。
3.可扩展性(Scalable)。软件必须能够在用户的使用率、用户的数目增加很快的情况下,保持合理的性能。只有这样,才能适应用户的市场扩展得可能性。
4.可定制化(Customizable)。同样的一套软件,可以根据客户群的不同和市场需求的变化进行调整。
5.可伸缩 (Extensible)。在新技术出现的时候,一个软件系统应当允许导入新技术,从而对现有系统进行功能和性能的扩展。
6.可维护性(Maintainable)。软件系统的维护包括两方面,一是排除现有的错误,二是将新的软件需求反映到现有系统中去。一个易于维护的系统可以有效地降低技术支持的花费。
7.客户体验(Customer Experience)。软件系统必须易于使用。
8.市场时机(Time to Market)。软件用户要面临同业竞争,软件提供商也要面临同业竞争。以最快的速度争夺市场先机非常重要。

  iOS 架构

(转自作者:MrPeak)

第一类:精简型应用架构

这类架构的文章分析主要还是围绕MVC展开,以苹果自带UIViewController优劣为出发点,再结合主流的MVP,MVVM,MVCS等变种进行分析演变。这类的探讨重点在于M,V,C三类角色的定义以及之间的数据事件流向的规范。很多小型应用所面临的问题及其架构层面的解决方案都集中在这一类。

第二类:综合型应用架构

对于用户量级在千万级或以上的应用来说,MVC这一层面的思考已无法应对业务疯狂增长所带来的负担。这类应用往往需要专业资深的架构师出面进行深层次的思考设计,业内不少大厂如淘宝,天猫,携程等都做过一些分享。不过到了这一层级的战斗,不光考验架构师的技术积累,更重要的是架构师对于业务的整体理解。我姑且把这类架构名之为:综合型应用架构。综合型应用架构一般不会提到MVC,更多是在探讨“层”与“模块”的划分和耦合。

第三类:深度优化的综合型应用架构

综合型应用架构是应对大规模业务增长的必经之路,一旦架构成型,后期业务膨胀会不停的打磨架构本身,产品本身对体验质量的追求会要求架构师和技术团队不停的优化架构细节。这种优化可以分为两块,第一是组件或模块划分的粒度越来越细,第二是组件模块的深度优化,比如网络层的深度优化,sqlite优化(多线程,FTS,安全等),数据加密,HotFix,Hybrid等,一些开源的第三方库已不能满足要求,需要团队自己重造轮子。这一层面的架构设计涉及面广,对架构师,团队技术人员的技术深度和业务理解能力有较高要求,短短一篇技术文章往往只能走马观花的介绍个大概,每一次优化几乎都可以作为一个专题来讲解。

第四类:组织型应用架构

这类架构在第三类的基础之上更进了一步,除了关注系统层面的架构设计之外,更对团队或部门之间协作方式,各系统模块的演进方式,产品发布流程等都做了规范。除去业务膨胀带来的压力,人员增长,各团队协作依赖增强等都会对app的质量,迭代速度产生影响,这些问题也需要从架构层面去解决。这类结合技术架构和组织架构的分享还比较少。

以上四种类型的架构又可以看做一般App从简至繁,公司规模随之增长的演进过程,技术圈绝大部分的架构类分享文章都可以归为上述四类。

第一类样本:

如果一篇架构的文章是以探讨MVC为起点,很有可能作者所说的架构就可以被归为第一类样本。

Xcode自带的UIViewController模板经常被戏称为“Massive View Controller”,主要是因为UIViewController除了负责Controller生命周期回调之外,还承担了View和Controller的工作。不少架构的探讨以此为基础,将Controller的M,V,C三个角色重新进行划分,又或者衍生出MVP,MVVM,MVCS,VIPER等其他组织方式。重新定义MVC之后,网络访问,持久化,安全等通用功能模块往往就由Controller或者Presenter负责了,这些负责业务逻辑的功能类直接依赖于成熟稳定的第三方基础库,比如AFNetworking,FMDB等。

但是,应付过复杂庞大业务模块的工程师会会有经验,无论以何种方式组织Controller,数据流向和事件传递再清晰合理,单纯代码量的膨胀就足以让Controller变得难以维护。MVC,MVP,MVVM都可以变得Massive。想象一下将10本书放在床头,你可以按翻阅频次,阅读习惯将书合理摆放,但当你面对100本书的时候,已不是如何摆放的问题,而是需要一个新的书柜。说到这里我挺服VIPER的,五个角色划分,尝试可以在床头放下100本书的Pattern。我们先来看看“10本书”的样本。
样本名:iOS 架构模式–解密 MVC,MVP,MVVM以及VIPER架构
英文原版中文翻译版
作者:Bohdan Orlov

这篇文章是第一类样本的典型,综合对比了MV(X)系列架构。我们来看下主要论点。

论点摘要:

文章认为优秀的架构具备以下三个特质:

Balanced distribution of responsibilities among entities with strict roles.

能把代码职责均衡的划分到不同的功能类里。

这是我们关注架构的原动力,有个粗浅的评判标准,同一个业务单位(Controller)里面,不能一个类1000多行代码,另一个类却只有10来行。当我们考虑网络请求的代码是该放在controller当中还是model当中的时候,在“代码量”上要做到“雨露均沾”,不能少数类“营养不良”。所以不论你的MVC,MVP或者VIPER是如何划分职责的,各个角色所承担的职责应当看起来是均匀分配的。

Testability usually comes from the first feature.

方便测试

测试是个有趣的话题,不同公司实践差异很大。我知道有些团队的产品质量严重依赖于QA团队,有些却干脆没有测试团队,完全依赖于开发人员自己保证质量,这两种风格一般取决于CTO的技术决策,和公司大小倒没多少关系。对于开发人员自测的情况,如果代码架构清晰,各角色职责分配合理,易测性是随之而来的副产品。

Ease of use and a low maintenance cost

易用且方便维护

易用是针对团队里的开发人员而言的。其实到底是用MVP还是MVVM,或者MVVM里数据如何流动,这些具体的规范到底采用何种形态不是最重要的问题,关键是在团队所有成员能有一致的认识,无论是写代码还是Debug都能快速切入。

围绕上面三个特质,作者一一分析对比了传统MVC,Apple MVC,MVP,MVVM,VIPER在这三方面的表现,结论是到底选用哪个纯粹是个人口味问题。相较于结论,作者对各个Pattern的角色定义及分析才是真正有价值的部分,我们在阅读评断其他类似架构文章的时候,也应该重在了解作者对于角色的理解。大家可以基于这一点,阅读下其他一些类似的文章。VIPER理论上并不能归类于MV(X)系列,而是突破了MVC的思考方式,比如定义了Router处理页面流程,这对架构师是个很好的思路,在对MV(X)重新设计的时候也应该能有新的思考,所谓“君子不器”。

MV(X)都是以MVC为原型进行变化,MV(X)到底如何使用没有教科书式的标准答案,关键在于架构师和团队各成员能达成一致的认识,在遇到问题时能不断的做调整重构,遇到难以跨越的瓶颈时,能跳出MV(X)寻找解决方案。

第二类样本:

在开始分析第二类样本之前,有几个概念比较重要,是我们深入讨论各个综合型样本的前提。组件,模块,层的定义。

在我看来,组件是比模块更小的功能单位,不具备业务属性,只处理基础通用的问题,类似于工具箱。比如我们给NSString写的Category提供base64,给NSDate写的Category做日期格式化等等。

模块较之组件粒度更大一些,另外最重要的区别是带有业务属性,和业务场景相关联。比如购物车模块,注册登录模块,支付模块等等,模块往往会对一些通用的组件产生依赖。

层是另一个维度的概念了,我平常阅读技术文章的时候,发现很多人会把模块和层的概念混淆,可以用一张图来区分模块与层的概念:

对于层不允许跨层访问,也就是说A不能直接访问C,下层不允许访问上层,B无法知道A的实现细节。层的概念可类比TCP/IP协议栈。模块就灵活多了,A,B,C三者之间可以任意访问依赖。所以一般来说层对架构的约束更高,但使得依赖更规范好维护,模块在复杂的场景下很容易结成一个网状的依赖形态,难以维护。第二类项目架构都是围绕层与模块的划分所展开,有的有严格的层次结构,有的不分层次,通过接口严格规范各模块交互,有的二者结合,在某一层内再做模块划分,下面我们分析的样本都属于上面三种情形。在开始分析之前,建议大家全篇了解下iOS开发圈关于组件化的讨论。

 

样本名:饿了么移动APP的架构演进

原文地址

作者:圣迪

这篇架构演进的讲解是典型的从第一类到第二类的过渡,可简化为以下几步:

1.使用传统MVC快速迭代App。

2.业务发展,有多个App需要开发,开始组件化,使用CocoaPods进行组件管理,目标是并行开发和代码重用。架构图如下:

如果读过上面关于组件化的文章,这张架构图就很好理解了,在做好模块划分和管理之后,通过Router的方式将让各模块产生耦合。这种架构方式已初步具备一个应付大规模业务增长的架构模型。

3.Hybrid,几乎所有运营类主导的App都会最终投入Hybrid的怀抱,运营团队对快速上线的要求只能在H5或者Hybrid上得以实现,不过这和架构本身关系不太大,是另一个大的话题。

4.React-Native & Hot Patch,这和第三步类似都是由运营驱动,几乎是所有内容运营型app的必经之路。

这四步的演化文章介绍的还比较粗略,更多的细节(比如Router内部实现,业务组件和非业务组件的分工,组件间耦合的方式等)没有介绍,或者饿了么App端也还在探索当中。

  第三类样本:

样本名:携程移动APP架构优化之旅

原文地址

作者:陈浩然

携程App端的架构演化在饿了么架构基础之上,多了很多优化的细节。

提到了对于基础SDK组件和业务组件的具体划分:

  核心功能SDK化

• 通讯、定位、Hybrid、数据库、登录、分享、基础库等

• 直接提供给其他BU独立App使用

  公用业务功能组件化

• 地图、日历、城市、图片、通讯录等13个公共组件

• 减少各BU重复开发工作量

性能数据指标采集:

• 网络性能:网络服务成功率、平均耗时、耗时分布

• 定位:获取经纬度成功率、城市定位成功率

• 启动时间、内存、流量等指标

• 多种纬度:系统、App版本、网络状况、位置等

网络优化

• 使用TCP长连接实现网络服务

• 根据网络状况2G/3G/4G/WIFI进行调优参数

• 根据连接/读/写不同阶段使用重试机制

• 使用IP列表避免DNS解析失败或者劫持

• 根据网络延迟选择服务端IP(使用Ping)

• 使用ProtocolBuffer+Gzip减少Payload

还有Hybrid,HotFix,React Native等就不一一列举了,这些细节性强的分享有很高的学习价值,能对刚涉足某一块优化的架构师起到方向指引的作用。(这些其实是很有意思的东西)

这些看似简单的划分往往很考验架构师的大局观和经验,涵盖面和合理的粒度掌控才能让技术团队的开发工作高效并行。

 

第四类样本:

样本名:Facebook iOS Architecture

视频地址1 视频地址2

作者:Facebook

除了和上面样本类似的模块化,组件优化的介绍之外,还有两方面新的信息:

在关于Infrastructure的介绍当中有张示意图介绍团队分工:

视频当中不光介绍了基础组件,业务模块的划分,和说明了这些划分对应的团队是如何进行协作。

  • 最底层的是基础设施团队,负责基础SDK的开发,可以对应之前谈到的非业务组件。
  • 中间层是业务团队,按业务模块进行划分,一个业务对应一个团队,团队负责全平台的开发,业务团在开发过程当中可反过来回馈基础SDK。
  • 最上面是产品的发布团队,产品发布团队配合业务团队对产品的发布周期和质量做保障。

在Facebook iOS客户端架构设计当中有关于model层介绍:

之前看过不少架构分享文章,少有对model层深入探讨的,一般提到都是一笔带过。model层涉及app数据持久化,以及runtime的状态维护,对app的稳定性和迭代效率起到至关重要的影响。

Facebook的model layer采用的是immutable策略,model虽然能被各层访问,但model的修改统一由model layer提供接口。上层业务团队一旦想修改某个model property,需要向model layer维护团队提出申请,这一设计对状态的维护相当谨慎。

好了,晚安。

 

发表评论

邮箱地址不会被公开。 必填项已用*标注