iOS移动端架构的那些事

作者: kuailejim | 来源:发表于2016-05-04 21:54 被阅读16343次

    引言:一个app的初始阶段,必然是先满足各种业务需求。然后,经过多次版本迭代之后,先前的由于急于满足需求而导致的杂乱代码则会充斥整个项目。而此时,项目有了一定的规模,有了一定数量的开发人员,那么为了达到快速迭代版本的需求,则是需要有一个强大的架构来支撑。

    在开始谈app架构之前,曾经我一度认为,一个好的app就是需要有好的架构,如果没有一个我所认为的“好架构”,那么这个app就是很low。
    直到去年参加北京ArchSummit时,听了无数的公司他们对于产品的架构之后,我陷入沉思,因为我总在自己的认知里选出一个自己认为最好的架构,然后觉得其他架构都是垃圾。
    静下心来想想,每个产品都有自己不同的定位,如果抛开它们的定位,抛开它们的业务需求去谈如果给它们设计一个良好的架构,这简直是扯淡。
    更何况很多优秀的app架构也是由一开始很弱而慢慢变得越来越强。
    所以没有最好的架构,只有适合自己的业务的架构才是最好的架构,并且它是逐步地变强变大。
    本文将举一个例子来演示这个过程。


    那么,到底什么是架构?

    架构,又名软件架构,是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。

    以我的理解,它就像是人的骨架一般,一个人从小生长到大,围绕着整个骨架去发展,变高变胖。
    可以把app开发看成一个汽车工厂的流水线,造车身->喷漆->组装等等。即把整个开发流程切成一个个模块,每个模块相互独立,并发工作。这就是所谓app架构。


    没有架构的“架构”

    某天,一个叫Jim的开发者,他打算开发一个app,当然有一定计算机基础的他知道采用MVC的设计模式来构造app,于是一个星期后,终于能跑起来一个app,但是此时,看一下项目的目录结构:

    v1.0
    嗯,不错,我们不但能run,还能看出这个app用了MVC的设计模式耶。
    但是随着开发的页面越来越多,一个月后,app有了10个页面,此时,打开Controller、View、Model这三个文件夹之后,发现每个文件夹里面竟然有几十个文件,它们杂乱无章的洒落在一起。此时不断有用户向Jim反映,xxx地方怎么按钮位置不对,xxx位置网络请求不成功。
    头痛的Jim才知道,当初应该把粒度分的更细,于是又了接下来的架构。

    分模块的架构

    Jim把不同的功能模块放在一块儿,于是得到了如下的架构:

    v2.0
    但是不对,一些工具类,公用类该放哪呢?Jim仔细思索了一番,于是又将上述架构进行改进,得到以下的架构:
    v2.1
    嗯,这看起来才像样嘛。

    Cocoapods

    慢慢地,Jim发现网上有很多可以现成拿来用的第三方框架,而他同时也学习了Cocoapods这个神器。
    什么是Cocoapods:

    CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has over eighteen thousand libraries and can help you scale your projects elegantly.

    它是一个能让你方便地管理第三方库的一个工具
    于是,项目变成了这样:

    v2.2

    此时的架构已经满足了个人开发者,或者说是小型开发团队的需求了。


    多人开发的架构

    但是在一个初具规模的公司,上述的架构模式是远远不行的,试想一下,如果有20个人同时开发一个app,而此时大家就算各自负责自己的模块,如果同时有不同模块的同学添加或者删除文件,对于.xcodeproj文件来说,会有严重的冲突。
    那么,怎么办呢?
    想一下cocoapods的作用吧,其实利用它能做很多很多事,我们完全可以把Home、Detail、Login等模块抽出来,也把它视为“第三方库”(实际上可以算是二方库)。初期阶段可以这么做:

    v3.0
    可以看到我们把Home这个模块抽出来了。
    这么做有什么好处?
    如果我们把各个业务模块都抽出来,首先来说,可以解决冲突的问题,并且业务团队之间的工作不受影响,并且可以并行开发,也无需再等待其他兄弟业务团队的进度了。
    当其他业务团队的任务完成时,我们只需pod update,将代码升级到最新就可以了。
    并且当一个公司有多个app时,如果有公用的模块,用这种方式会更优雅。
    当然,如果你觉得直接指定git地址太low,你完全可以搞一个私有Spec,专门存放业务模块代码。

    子project模式的架构

    很多人会说上述方式很复杂,还不如采用在主工程下中放子工程(业务模块)来实现抽象:

    不合理的架构
    但是我觉得这个方式管理起项目很麻烦,更新模块代码、不同app间复用模块代码都会没上述的方式优雅,所以我觉得这种方式没有上述的好。

    对各个模块进行解耦

    现在虽然我们对各个模块进行了抽象,但是业务模块间还是互相引用,对于开发来说,虽然解决了代码的耦合问题,对于代码的引用关系却没有改变:

    v3.0
    上图才只有4个模块,如果有上百个模块的话,这个关系可以复杂成一个庞大的蜘蛛网,这对于后期维护来说,成本是巨大的。

    所以我们想,有木有好的方式来解耦呢,当然是有的,我觉得以下两个方式是很好的:

    • middleman
    • urlRoute

    先来介绍middleman(中间人模式)

    middleman
    如图所示,我们只需将所有的模块依赖这个middleman,让middleman来处理各个模块的关系,模块A如果需要依赖模块B,完全可以考middleman来处理,并且返回模块A所需要的模块B的内容,这样就解决了解耦

    再来说说urlRoute,其实这个方式类似于Facebook很早前的320结构了。
    思路就是将每个页面看成是一个url,设置一套路由规则,在页面打开时将url进行路由查找,然后这样一来,只要模块A按照既定的规则写好url,那么模块B依赖模块A时,只需根据url打开就行了。

    urlRoute

    middleman VS urlRoute

    两种方式各自有自己的优点和缺点,他们主要的区别在于:

    • 传参的方式
    • 所需维护的具体内容不同

    我更倾向于urlRoute,为什么这么说呢?
    由于本文讲的是app架构,这里就简单介绍下:
    如果一个页面出现了问题,我们可以用各种patch的方式来打补丁。但是,如果一个页面出现了致命的错误,打patch的成本过于高的话,此时如果用urlRoute,则有个妙招。
    我们可以更改appConfig(app的配置,可以从服务端动态更新),更改页面所对应的url,并且改成在路由规则里跳到webview的规则。
    这样之后,当跳到此模块时,则会打开对应的H5页面,而不是原来的有问题的页面。
    当然middleman也可以处理成这样,但是从实现的角度来说,显然是urlRoute更为优秀。


    总结

    当然,架构远远不是一篇文章能讲清楚的,也不仅仅只是结构层次方面的内容,还有例如push、hotpatch、动态化、appConf、Service中间件模块的具体实现,MVC\MVVM\MVCS设计模式等等。
    本文只是从最基础的地方展现具体业务模块的解耦方式,希望能起到抛砖引玉的作用。
    个人微博:@kuailejim
    个人博客:http://kuailejim.com

    相关文章

      网友评论

      • 米匠:请问,用urlRoute模式,传递UIimage等对象类型怎么办?
      • 豆志昂扬:pods这东西鸡肋,除非遇到类库版本的问题,否则用子项目的策略就可以解决了。
      • crazyfox:写的不错,很有参考价值
      • aa676c2b66ca:没有最好的架构,只有适合自己的业务的架构才是最好的架构~~
      • Sunday_gao:那个subProject的可以换成 mainProject+framework1+framework2+cocoapods的方式
      • Junheng:好文值得一看
      • idage:不错
      • 戈多_于勒:好好好
      • 李国安:很棒
      • Mr_Lucifer:所以能不能给个 urlRoute 的 demo? 虽然说的很清晰, 但是还是不知道怎么写, 毕竟还没有十多人开发的经验.
        kuailejim:@Mr_Lucifer 有空会去写一个简单版
      • pengxuyuan:我想问下 在用中间人模式的时候 各个模块是依赖中间人的
        在用URLRoute时候 各个模块也是依赖URLRoute的吧?
        这样子那个URLRoute关系图的话 也应该有线连接吧?
        然后 这样子在引用关系上是一样的吧 URLRoute更加灵活而已吧?
        请问是这样子理解吗 求指导 :sob: :sob: :sob: :sob:
      • 犯傻小二:写的不错,分析的也很彻底
        d312cc476a2f:@犯傻小二 good
      • Resory:66666
      • a558d5f37b23:能详细讲下URLroute吗
        司马捷:@勤严 嗯,这里能有个demo就更好了
      • 鱼鱼鱼四只鱼:说的很易懂,谢谢
      • __Null:有demo不?
      • puppySweet:iOS上内置相机应用录制的mov/mp4视频竖屏拍照的时候产生一个Rotation元数据,表示录制视频时摄像头旋转到了多少角度。其值一般为这四个:0、90、180或270。类似于图片文件的Exif信息中的Orientation元数据。

        Rotation元数据用于播放器确定渲染视频的方向,但有的播放器会对其视而不见。 我竖屏用avaasertWriter录制视频 所以我现在 Rotation 是90度 但是 windows下 有些网站播放器播放时 会倒转90 度 因为有些windows下 的播放 不认得 Rotation这个属性 怎么办 不会自动旋转90自适应 苹果安卓手机播放一点事都没有 火狐浏览器的播放器也没问题



        网上的案例旋转回90度的方法不是用avassertWriter 录制视频

        而是avcapturefileouDAtavideo录视频 再用AVMutableVideoComposition 旋转 exprotSession等旋转后压缩倒出的 这导致 我用avassertWriter设置的比特流 帧绿 还有一些属性全变了

        怎么办怎么办
      • 51bitquant:iOS开发,一般公司都是3/5个人一下!因此没有太大的问题!但是中间人的设计架构模式听不错的!期待你的demo
      • 王大先森:代码代码 demo demo来一份额
      • 南栀倾寒:能把roter 写出来, 模块依赖基本解决了
      • ZuraX:不错不错,我做iOS之余写过一段时间flask,个人很喜欢路由模式。
      • _Finder丶Tiwk:请问您那个子project的架构怎么搞? 我之前试了一下 ,子工程只能是静态库工程吧?方便给个简单的demo 吗
      • zyg: 路过~
      • bigParis:写的不错,脱离业务谈架构确实有些扯淡,你说的middleman也是要依赖所有模块吧?
        bigParis:@kuailejim 你说的映射方式应该是ClassFromString然后用运行时去调用吧?
        Colin_狂奔的蚂蚁:@kuailejim 这个应该就是组件化方案吧,我的项目就是采用的组件化方案解决页面跳转和传参的
        kuailejim:@bigParis 不用的 可以用映射的方式不引用
      • Ryan文濤:由于现在在公司里都是一个人或者两三个一起写app的,所以没有遇到多人合作的麻烦,但是也想知道,假如多人合作(10人)以上一起开发时候的做法,有个demo来参考一下就最好了。
        kuailejim:@Ryan文濤 嗯 这个东西比较抽象 哈哈
      • cz3w:学习,如果能集合代码,给出一个最小的例子就更好
        kuailejim:@cz3w 下次努力给一个

      本文标题:iOS移动端架构的那些事

      本文链接:https://www.haomeiwen.com/subject/ehurrttx.html