iOS架构小记

一、概述

1、背景
  • 谈及iOS的架构,绕不开组件化、MVC、MVVM这些关键词
  • 对于几个人的小团队,组件化未必是适合的方案,当然遇到业务扩张,新App需要落地,将相同的功能沉库,可以汲取组件化的精髓,提高生产效率。
  • 组件化是大中型App团队的选择。
  • 对于百人以上的客户端超级大团队,组件化非常值得拥有,当然实施起来也非常复杂,包括业务划分,业务组件和基础组件的设计和开发、测试、集成和发版。
2、MVC和MVVM
  • 好像一说起Controller代码臃肿,就把锅求给MVC,就鼓吹MVVM,其实MVC虽然有明显的缺点,但是合理使用,绝对满足大部分需求,造成代码的臃肿和难以管理,很大原因和开发者认知有关系。
  • MVVM可以用,使用MVVM不必要一定引入RAC,
3、组件化
  • 业务快速发展、复杂的业务场景、团队的快速扩张等带来的变革诉求,希望通过组件化来提高团队的协作能力、减低开发成本,提高开发质量。
  • 组件化直接效果:代码解耦,功能模块化;代码的复用性高;代码管理更加科学。
  • 蘑菇街(蘑菇街 App 的组件化之路)、支付宝(从支付宝红包揭秘亿级APP的移动开发)等团队公开的信息来看:业务的增加、开发团队的扩张、快速迭代的要求,催生组件化方案(可能有更好的架构方案)快速落地。
  • 蘑菇街和支付宝等团队实现组件化方案,一是业务发展的必然选择;二是其技术沉淀深,能为自己和兄弟团队打造出质量上乘的组件化服务。

二、组件化实施

1、实施步骤
  • 剥离产品公共库和基础库
1
将网络请求、数据存储、图片下载和存储、第三方SDK(微信,支付宝)、UI基础组件、自定义相机等、UIKit和Foundation的扩展等,拆分出来,各自沉库,使用cocopods管理。
  • 独立业务模块单独成库
1
将登录、分享、支付、日志上报等模块封装成组件,也可以将一些通用模块,如资讯详情页、XX模块拆分出来,拆分粒度可以先粗后细,将相对独立的功能封装成组件,统一对外提供服务,保证体验一致性。
  • 对外服务最小化
1
在前两步都完成的情况下,根据组件被调用的需求来抽象出组件对外的最小化接口(遵循SOLID原则中的接口分离原则)
2、组件通信

组件之间的通信,更多是指业务组件之间的通信吧。目前蘑菇街团队公开的方案是:URLRouterProtocol Class BindingTarget-Action这三类方案。

1)URLRouter
  • 简介:蘑菇街团队实现MGJRouter库,可以根据URL处理执行对应的Block;其核心在于,先注册URL 和 服务Block & 参数字典的对应关系(保存在router字典中),然后利用URL找到对应的Block,将参数字典交给Block,唤起对应的服务。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 注册
    [MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) {
    [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]];
    }];

    //传参
    [MGJRouter openURL:@"mgj://foo/bar" withUserInfo:@{@"param1":@"hello world"} completion:nil];

    //同步获取object
    NSNumber *orderCount = [MGJRouter objectForURL:@"mgj://cart/ordercount"]
  • 优势:解耦方便;各个组件依赖MGJRouter就可以;打破组件间的相互依赖;

  • 不足:组件本身依赖中间件,但是分散注册又使得耦合较多
    需要专门维护URL(蘑菇街使用后台维护,自动生成URL短链的方式);

  • 补充MGJRouter是URL Router的OC版本实现,URLNavigator是URL Router的Swift版本实现,核心都是:注册URL和对应的处理,然后根据URL解析去做事情,如页面跳转。

2)Protocol Class Binding(协议和类绑定)
  • 简介:核心在于,为组件定义Protocol,Protocol指定返回的数据,然后在组件中新建Class实现Protocol,如此将Protocol和Class关联起来。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //以购物车组件为例
    //1、组件定义MGJCart,执行返回订单数方法
    @protocol MGJCart <NSObject>

    - (NSInteger)orderCount;
    @end

    //2、MGJCartImpl 实现MGJCart ,实现略

    //3、关联
    [ModuleManager registerClass:MGJCartImpl forProtocol:@protocol(MGJCart)],

    //4、获取MGJCartImpl,接下来可以访问到参数了
    [ModuleManager classForProtocol:@protocol(MGJCart)]
  • 优势:把公共的协议统一放到同一文件中,组件依赖该文件即可。

  • 补充:阿里的beehive属于此类方案实现,具体参考BeeHive —— 一个优雅但还在完善中的解耦框架,核心思想涉及:各个模块间从直接调用对应模块,变成以Service的形式,避免了直接依赖;App生命周期的分发,将耦合在AppDelegate中的逻辑拆分,每个模块以微应用的形式独立存在。

3)Target-Action方案
  • 简介: CTMediator方案,在此类中对外提供明确参数类型的接口,接口内部通过performTarget方法调用服务方组件的Target、Action。由于CTMediator类的调用是通过runtime主动发现服务的,所以服务方对此类是完全解耦的。但如果CTMediator类对外提供的方法都放在此类中,将会对CTMediator造成极大的负担和代码量。解决方法就是对每个服务方组件创建一个CTMediator的Category,并将对服务方的performTarget调用放在对应的Category中,这些Category都属于CTMediator中间件,从而实现了感官上的接口分离。
  • 特点: 侵入小,但硬编码较多,Runtime编译阶段不检查,运行时才检查对应类或者方法是否存在,对开发要求较高。
3、其他

需要一个组件的管理平台,管理这些组件的单元测试、集成和发版

参考文章:iOS App组件化开发实践iOS组件化方案选型

三、MVC

1、苹果推荐的MVC
  • 我们常说的MVC主要包括以下三部分
1
2
3
控制器(Controller)- 数据的加工者
视图(View) - 数据显示
模型(Model)- 数据管理
  • 在苹果推荐的MVC中,View和Model是没有通信的,但是Controller可以Model和View通信;

MVC

  • Controller是可以直接访问Model,然后Model不知道Controller是谁,当Model发生变化时候,利用通知、代理或KVO等方式通知Controller
  • Controller也可以直接访问View,Controller可以直接根据Model来决定View的展示。View接收到响应事件, 通过delegate、target-action、block等方式告诉Controller的状态变化。Controller进行业务的处理,然后再控制View的展示。

  • 随着Controller和Model、View的交互(通信)越来越多,Controller中的代码就越来越多,造成Controller中代码过于臃肿。

2、MVC的优化实践
  • MVC是非常经典的设计模式,虽然有很多问题,但是使用得当的话,其实能搞定大部分项目。很多时候,代码的臃肿很大原因是开发者代码不规范造成的,比如将网络请求逻辑,数据的存储逻辑都放在Controller中,或者Controller中展示聚集太多的UI元素,这如何不造成Controller的代码臃肿呢。
  • 在使用MVC模式,需要划分好MVC的职责
1
2
3
4
5
6
7
8
9
10
11
12
13
Model应该做的事:
1.给ViewController提供数据
2.给ViewController存储数据提供接口
3.提供经过抽象的业务基本组件,供Controller调度

Controller应该做的事:
1.管理View Container的生命周期
2.负责生成所有的View实例,并放入View Container
3.监听来自View与业务有关的事件,通过与Model的合作,来完成对应事件的业务。

View应该做的事:
1.响应与业务无关的事件,并因此引发动画效果,点击反馈(如果合适的话,尽量还是放在View去做)等。
2.界面元素表达
  • 对软件结构做好分层,将软件分成若干个水平层,每一层都有清晰的角色和分工,不需要知道其他层的细节。层与层之间通过接口通信。

四、MVVM

1、介绍

MVVM.png

  • 在MVVM中,将和视图、数据的交互处理从Controller中移到ViewModel中;VM负责和Model通信,可以直接访问Model,当Model改变时候通知VM;同时,VM负责和视图的通信,直接访问视图,当视图接收到响应事件时,通知VM去处理。而Controller仅仅是协调各个部分的绑定关系以及必要的逻辑处理。
  • MVVM的核心是:实现MVVM的双向绑定,很多项目组因此引入RAC框架(函数响应式框架),其实使用KVO机制也可以实现双向绑定也可以,直接使用KVO,会有一些问题,建议直接使用facebook的KVOController
2、MVVM和MVC的对比

mvvm和mvc.png

  • MVVM优点:方便测试,VM可以方便做单元测试,MVC下的Controller里面逻辑太多,无法做单元测试;耦合度低; 复用性高; 层次更清晰; 重构成本低;
  • MVVM缺点:类文件增多(每个VC多一个ViewModel),ViewModel的代码复杂度增大(处理各种交互)
  • MVC优点:通用架构; 处理耦合度高的逻辑方便;
  • MVC缺点: 耦合度高; 复用性差; 测试性差;
3、补充个MVP
  • MVP分别是ModeViewPresenter,对应数据视图主持者,在Android开发中使用普遍。

MVP.png

  • MVC模式下,Android的Activity承担繁杂的业务逻辑,导致代码臃肿,使用MVP模式,将View层和Mode层隔离开,增加Presenter层,作为Mode层和View层通讯的桥梁
  • Presenter同时持有Mode层和View层的引用,在需要数据改变 或 视图显示时直接改变数据或者视图的显示状态。同样View层持有Presenter层的引用,这样就能将一些处理事件的逻辑放在Presenter层中进行处理,处理完成后通知View层改变显示状态。
  • 具体参考 MVP模式的经典封装

五、SOLD原则

1、介绍
  • 根据项目的复杂度来决定选择MVC或MVVM,但是无论哪种模式,都做好代码设计,结构分层,类设计遵守SOLID原则
缩写 全称 中文 备注
S The Single Responsibility Principle 单一责任原则 一个类只应承担一种责任
O The Open Closed Principle 开放封闭原则 可扩展,不可修改
L Liskov Substitution Principle 里氏替换原则 子类可以在任意地方替换基类且软件功能不受影响
I The Interface Segregation Principle 接口分离原则 将接口拆分成更小和更具体的接口,有助于解耦
D The Dependency Inversion Principle 依赖倒置原则 一个方法应该遵从“依赖于抽象而不是一个实例”。依赖注入 是该原则的一种实现方式。
2、IOC和DI

控制反转(IOC)和依赖注入(DI)是Spring中最重要的核心概念之一,而两者实际上是一体两面的。

  • 依赖注入
    • 一个类依赖另一个类的功能,那么就通过注入,如构造器、setter方法等方式将这个类的实例引入。
    • 侧重于实现。
  • 控制反转
    • 创建实例的控制权由一个实例的代码剥离到IOC容器控制,如xml配置中。
    • 侧重于原理。
    • 反转了什么:原先是由类本身去创建另一个类,控制反转后变成了被动等待这个类的注入。
文章作者: 南华coder
文章链接: http://buaa0300/nanhuacoder.com/2019/02/21/iOS-iOSDesignMode/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 南华coder的空间