02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

前言

从前一篇 文章 ,咱们环绕 “移动客户端架构规划” 这个话题打开评论的过程中,咱们做了根本的总述,而且针对 架构规划 的大话题简扼地区别为五个部分:

  • 模块化开发
  • 组件化开发
  • 二进制化处理
  • 规划办法
  • 架构规划与架构演进 咱们将在本篇文章,进一步对 模块化开发 这个话题 深化打开 评论

咱们先简略回忆一下,在前面的总述中介绍 移动端模块化开发 之前咱们介绍的部分常识

项目周期简述

一个项目从立项到开发流程根本能够扼要概括为:

  1. 开发一个软件体系,首先需求确定需求
  2. 并依据需求对软件体系进行必定的分层规划
  3. 在层次区别的基础上进行模块区别
  4. 模块区别往后便是挑选组件用于集成完结模块
  5. 集成->开发->测验->布置上线……
  • 模块区别之后,咱们需求做的作业便是模块挑选模块规划
    • 模块挑选1: 公司的现存开发套件的模块,依据已有模块的基础上进行恰当改造迭代
    • 模块挑选2: 收购 厂商的SDK 服务,如前面提及的 mPaaS 蚂蚁金融云服务等
    • 模块挑选3: 去获取开源第三方模块,并依据此项目需求,进行恰当改造
    • 模块挑选4: 从头规划开发

模块化开发:

  • 【模块化开发】 指的是: 将一个体系或运用程序区别多个功用模块
  • 每个模块具有独立的功用接口,而且能够单独开发测验保护
  • 【模块化开发】意图是为了进步代码复用性可保护性可扩展性,以及下降开发测验的复杂度难度

组件化开发:

  • 【组件化开发】 则是在模块化开发的基础上,将模块进一步区别为更小的组件
  • 每个组件有自己的界面事务逻辑,而且能够独立布置和升级
  • 【组件化开发】 的意图是为了进一步进步代码复用性可保护性,一起还能够完结更多的代码并行开发更灵敏的运用程序构建发布
  • 模块化开发组件化开发 都是为了进步软件开发的功率和质量
  • 组件化开发相关于模块化开发 愈加细粒度灵敏能够更好地满意大型运用程序/软件体系的的需求(换言之,一个模块能够集成若干组件,以完结模块的功用)
  • 个人总结,从规划上来看
    • 模块强调责任(内聚、别离)
    • 组件强调复用

模块规划:

无论是组件开发仍是模块开发,咱们都需求对其进行必定的规划作业,尽量防止不必要地重复作业和回避一些开发危险。

咱们能够经过StarUML这个建模东西进行软件的规划作业,StarUML支撑:

  • 类图时序图用例图等十几种图形办法;
  • 能够经过我 这篇文章初步了解StarUML
  • 能够经过 官网 进一步学习StarUML的运用

总述

本文介绍的内容适用于项目完结了需求确认,软件分层规划之后的阶段。

在完结了软件分层架构规划之后,需求将模块区别归类,进一步合理规划软件架构,并凭借模块办理结构,完结App模块化开发以到达App的快速集成项目落地的技能计划。

本文内容结构

模块分类:
罗列市面上盛行的不同类型几款App,对模块进行区别

  • 依照【事务领域】区别;
  • 依照【功用服务】区别;
  • ……

模块区别
介绍模块区别的战略

  • 模块分类 是区别的结果
  • 模块区别 是分类的战略

了解几种模块化计划

  • 罗列几种模块化计划
  • 剖析不同计划的好坏

模块功用规划

  • 参考的模块化才能
  • 定制模块化才能
  • 理论剖析
  • 规划模块功用

模块化开发实践

  • 开发模块办理结构
  • 开发模块,实践模块办理与集成

一、模块的三级分类

    1. 金融-付出宝事例:

1. 第一级模块】: 运用级模块

以【金融-付出宝】为例剖析:

模块区别:

  • 第一级模块】: 运用级模块:
    • 主页
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 理财
    • 日子
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 音讯

2. 第二级模块】: 事务服务模块

以主页为例

  • 主页模块
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    点击主页的更多进口,进入运用中心,咱们能够看到若干事务功用模块,付出宝很好地为他们做了归类:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 【便民日子】:
    • 事务模块: 充值中心日子缴费交管12123
  • 【购物娱乐】:
    • 事务模块: 饿了么彩票淘宝
  • 【财富办理】:
    • 事务模块: 花呗借呗基金股票
  • 【教育公益】:
    • 事务模块: 蚂蚁森林运动

3. 第三级模块】: 功用模块

以便民日子大类的充值中心为例

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

  • 事务模块:充值中心
    • 事务1: 话费充值
    • 事务2: 流量充值

咱们经常运用付出宝,就不难发现, 简直一切的 服务型 事务模块,都离不开一个功用,那便是 付出

此处 的 付出功用 便是 付出功用模块

付出宝的实质便是 移动付出东西 , 其它 商业性事务 都是 环绕其 即时、快捷的 电子付出功用 向广阔用户 供给 日子中的各类服务的。

付出宝只需一个 功用模块

那如此说来,是不是付出宝就只需一个功用模块, 付出模块 呢?
答案必定是 否定 的!!!

付出宝完结其付出模块的功用,最起码完结了以下几个功用:

  • 网络通讯功用
  • 加密功用
  • 绑定银行卡,还用到了OCR辨认功用、绑定验证时短信服务功用
  • 开启快捷付出时的人脸辨认功用
  • 还有为了 监控 运用 运营状况, 处处都要用到的,埋点功用

朋友们,若你还分不太清各级模块, 你也能够试着找一些运用来进行模块区别,欢迎留言,咱们一起探讨!

其他事例剖析引荐运用:

    1. 电商: 淘宝
    1. 教育: 中公教育、六分钟英语
    1. 医疗: 微医
    1. 车联网: 广汽传祺、哈啰出行&&嘀嗒出行
    1. 通讯: 中国移动
    1. 东西: AudioTools
    1. 娱乐: 抖音、网易云音乐
    1. 物联网: 海尔家居、华为手环
    1. 日子: 美团、菜鸟裹裹

二、模块区别战略

1. 区别战略

咱们从模块的三级分类事例中,能够总结得出结论:

  • 模块ke分三大类: 运用级模块事务级模块功用级模块
  • 运用级模块:
    个数取决于App自身的规划者,是选用由几大运用模块的丰厚版 或是 单一运用模块的简约风格
    • 可 集成 若干 事务级模块
    • 有必要集成 用户模块 :运用 用户数据进行 事务交互
    • 单运用模块App示例: 打车服务途径:嘀嗒出行
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 多运用模块App示例: 打车服务途径:哈啰出行
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 事务级模块:
    • 运用级模块下,有若干个 事务级模块
    • 事务级模块示例:
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 事务级模块 事务模块便是 公司 商业活动的各种途径
      • 用户的消费途径(于企业主而言)
      • 广告途径(于企业主而言)
      • 运用途径(于用户而言)
      • …..
    • 其 完结, 至少依靠 一个 功用级模块
    • 其 完结, 需依靠 必定的事务UI组件
    • 其 完结, 可依据事务场景需求定制
      • 规划 事务服务
      • 规划 服务事务页面
      • 规划 场景交互
  • 功用级模块:
    • 小功用,自身可独立供给功用服务
    • 大功用,须依靠 若干小功用
    • 大功用 便是 小功用组

2. 常见的模块归类

依照【运用级】区别:

  • 付出宝:
    • 主页
    • 理财
    • 日子
    • 音讯
    • 我的
  • 淘宝:
    • 主页
    • 逛逛
    • 音讯
    • 购物车
    • 我的
  • 高德地图:
    • 主页
    • 附近
    • 音讯
    • 打车
    • 我的
  • 美团:
    • 主页
    • 优选
    • 音讯
    • 购物车
    • 我的
  • 抖音:
    • 主页
    • 朋友
    • 发明(符号模块:+号)
    • 音讯
    • 我的
  • 哈啰出行:
    • 主页
    • 车主
    • 逛逛
    • 钱包
    • 我的
  • 嘀嗒出行:
    • 乘客运用模块
    • 车主运用模块
  • …….

依照【事务服务】区别:

  • 【事务模块】: 金融服务、电商服务、咨询事务、通讯事务、物联网事务等
    • 【金融事务】: 基金事务、理财事务、保险事务、存款事务、贷款事务、汇款事务等
    • 【电商事务】: 物流事务、下单事务、产品墙、商品概况展示与交互等
    • 【通讯事务】: 智能客服、音视频通讯、无人机控制等
  • 【物联网事务】…..
  • ….

依照【功用级】区别:

  • 【服务模块】:第三方途径服务、第二方途径服务、公司途径服务
    • LBS、社会化共享、广告服务、推送服务、即时通讯服务、埋点服务、电子付出服务等
  • 【加密模块】:
    • 公司方加密(前后端算法校验): 算法加密
      • MD5
      • RSA
      • AES
      • DES
      • GMx
      • Hash
    • 二方加密途径服务(需收购): 证书加密、蓝牙Sim盾、Okta双重验证
    • 本地加密生物信息采集: 人脸辨认、手势辨认、面容辨认
  • 【通讯模块】: 网络通信、无线物联网通讯
    • BLE通讯+iBeacon通讯、WIFI通讯等
    • https/http网络通讯、socket/websocket通讯等
  • 【硬件特性】: 拍照功用、OCR辨认功用、人脸辨认功用、音视频功用、陀螺仪等传感器等
  • ……

3. 了解相关的技能概念以及命名标准约定

3.1 顶层概念

概念
Application 运用程序: 模块化开发中咱们用SubApp作为运用模块的关键词
Feature 特性: 模块化开发feature作为事务模块的关键词。咱们开发上线一个新事务模块常常也称之为新特性
Capability 才能: 模块化开发capability作为功用模块的关键词
Component 组件: 模块化开发component作为事务组件功用组件的关键词
Plugin 插件: 模块化开发plugin作为二方库三方库公司途径库SDK服务/功用插件 的关键词。常见的服务:authentication(OAuth授权登录)、bot protection(防机器人爬虫)等
Foundation Libraries low-level open source and commercial libraries

3.2 二级概念

Feature 事务模块命名举例:
Finance 金融事务: feature.finance
Commerce 电商事务: feature.commerce
Life 日子服务: feature.life
XXX服务: feature.xxx

3.3 三级概念

Capability 功用模块命名举例:
Live 直播功用: capability.live
LBS LBS功用: capability.lbs
Network 网络功用: capability.network
Persistence 数据缓存功用: capability.persistence
Analytics 埋点剖析功用: capability.analytics
XXX功用: capability.xxx

四级概念

关于组件的概念以及相关的区别办理规矩,会在讲解组件的姊妹篇文章,进行深化论述,本次不铺打开来介绍

Component 组件命名举例:
Product carousels component.productCarousels
Size pickers component.sizePickers
Story cards component.storyCards
XXX组件: component.xxx

4. 小结

咱们学高数的时分,总能听到教授想念那几句华罗庚的经典语录:

  • 数缺形时少直观,形少量时难入微;
    数形结合各样好阻隔分居万事休
    这句话的意思是, 直观的 图形映像 + 逻辑化的数学公式映像 彼此结合 才能 更好地知道了解且相对回忆深刻。

4.1 架构分层图

所以,咱们无妨在这里来搞一个 具象化 的 小结:

  • 仍然以 付出宝为例: 三级模块区别的细节,同学们能够回到section1回忆一下
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 付出宝架构分层图:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 运用层: 事务模块
    • 服务层: 事务服务功用模块
    • 容器层: 结构服务功用模块
    • 结构层: Native App FrameworkH5 App Framework 是归于蚂蚁团队 对组件封装 的中间层介质,为 服务层、运用层 供给 调用结构层的接口
  • 区别结构Tree:
    
     - App
        |
     - - - - feature = [feature1 = Capability1 + Component1]+[feature2 = Capability1 + Component1]...
                |
     - - - - - - - - Capability、Component:  【Capability = Plugin + Capability完结封装】、【Capability = 纯Capability】、【Capability = Component+Component+Component+Component...+ Capability完结封装】
    
    

4.2 模块区别层级图:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

  • 运用结构分级结构:
    • 超级App
      • 事务模块1
        • 事务组件1
          • 【UI组件+功用模块】组合1
            • 【通用UI组件+功用模块】
          • 【UI组件+功用模块】组合2
            • 【定制化UI组件+功用模块】
              • 功用模块1
                • 插件1
                • 插件2
              • 功用模块2
        • 事务组件2
      • 事务模块2
      • ……

三、了解几种模块处理计划

1. 模块间有复杂的依靠联系:

一个 APP 有多个模块,模块之间会通信,彼此调用;

  • 例如微信读书有 书本概况 想法列表 阅览器 发现卡片 等等模块,这些模块会彼此调用
  • 例如 书本概况要调起阅览器和想法列表,阅览器要调起想法列表和书本概况,等等,一般咱们是怎样调用呢,以阅览器为例,会这样写:
    #import "WRBookDetailViewController.h" 
    #import "WRReviewViewController.h" 
    @implementation WRReadingViewController 
    - (void)gotoDetail { 
        WRBookDetailViewController *detailVC =  [[WRBookDetailViewController alloc] initWithBookId:self.bookId]; 
        [self.navigationController pushViewController:detailVC animated:YES]; 
    } 
    - (void)gotoReview { 
        WRReviewViewController *reviewVC = [[WRReviewViewController alloc] initWithBookId:self.bookId reviewType:1];
        [self.navigationController pushViewController:reviewVC animated:YES]; 
    }
    @end
    
  • 看起来挺好,这样做简略明了,没有多余的东西,项目初期引荐这样快速开发,但到了项目越来越庞大,这种办法会有什么问题呢?
  • 清楚明了,每个模块都离不开其他模块
    彼此引证,彼此依靠,耦合严峻:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 模块之间这样严峻耦合,对测验/编译/开发功率/后续扩展都有一些坏处;

2. 模块间彼此通讯的意图:

模块间彼此通讯的意图,总结就以下几个:

  • 获取模块实例
    • 页面跳转
    • 页面传值
  • 运用模块的事务服务/功用服务
  • 传递响应者链信号
    • 捕获时机,履行相关逻辑

3. 那怎么解耦呢?

按软件工程的思路,咱们一看就知道应该加一个中间层

为了处理模块彼此依靠耦合严峻,广阔码友们做过的一些尝试:

加了中间层后,必定程度脱节解耦后的几种依靠办法:

  • 02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

咱们且称中间层Mediator,Mediator的责任便是模块间引证/通讯的意图。咱们无妨进一步笼统总结之为,在完结不同模块解耦的前提下,到达:

  • 1、获取模块实例
  • 2、在模块间”音讯传递”

那么这里有几个问题,咱们需求答复清楚:

  1. Mediator 怎么完结音讯传递
  2. 模块A 只跟 Mediator通信,怎么获取 模块B的接口?
  3. 模块间 的解耦 是经过Mediator 来完结的,模块Mediator 的彼此依靠,怎么削弱 乃至破除?

4. 经典解耦计划介绍

4.1 路由服务注册绑定办法:URL+HandlerBlock路由表办理

1.) 代表结构 MGJRouter
[MGJRouter工程目录]
|
|-[MGJRouter]
|        |-MGJRouter.h.m
|-[MGJRouterDemo]
|        |-[Actions]
|        |       |-Target_A.h.m
|        |-DemoModuleADetailViewController.h.m
|
|-AppDelegate.h.m
|-DemoDetailViewController.h.m
|-DemoListViewController.h.m
2.) 音讯传递的中间件
  • [MGJRouter]
    MGJRouter是担任音讯传递的中间件
  • 音讯传递办法:
    • register url 注册模块
      • 经过 url 绑定 一个 操作绑定Block
      • url格局 = 协议头://模块/参数列表
    • open url 翻开模块
  • 传递音讯的中间件源码API:
    #import <Foundation/Foundation.h>
    extern NSString *const MGJRouterParameterURL;
    extern NSString *const MGJRouterParameterCompletion;
    extern NSString *const MGJRouterParameterUserInfo;
    /**
     * routerParameters 里内置的几个参数会用到上面界说的 string
     */
    typedef void (^MGJRouterHandler)(NSDictionary *routerParameters);
    @interface MGJRouter : NSObject
    /**
     * 注册 URLPattern 对应的 Handler,在 handler 中能够初始化 VC,然后对 VC 做各种操作
     *
     * **@param** URLPattern 带上 scheme,如 mgj://beauty/:id
     * **@param** handler  该 block 会传一个字典,包含了注册的 URL 中对应的变量。
     *          假如注册的 URL 为 mgj://beauty/:id 那么,就会传一个 @{@"id": 4} 这样的字典过来
     */
    + (void)registerURLPattern:(NSString *)URLPattern toHandler:(MGJRouterHandler)handler;
    /**
     * 翻开此 URL
     * 会在已注册的 URL -> Handler 中寻找,假如找到,则履行 Handler
     *
     * **@param** URL 带 Scheme,如 mgj://beauty/3
     */
    + (void)openURL:(NSString *)URL;
    /**
     * 翻开此 URL,一起当操作完结时,履行额定的代码
     *
     * **@param** URL    带 Scheme 的 URL,如 mgj://beauty/4
     * **@param** completion URL 处理完结后的 callback,完结的断定跟具体的事务相关
     */
    + (void)openURL:(NSString *)URL completion:(void (^)(void))completion;
    /**
     * 翻开此 URL,带上附加信息,一起当操作完结时,履行额定的代码
     *
     * **@param** URL    带 Scheme 的 URL,如 mgj://beauty/4
     * **@param** parameters 附加参数
     * **@param** completion URL 处理完结后的 callback,完结的断定跟具体的事务相关
     */
    + (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(void))completion;
    /**
     * 是否能够翻开URL
     *
     * **@param** URL
     *
     * **@return**
     */
    + (BOOL)canOpenURL:(NSString *)URL;
    /**
     * 调用此办法来拼接 urlpattern 和 parameters
     *
     * #**define MGJ_ROUTE_BEAUTY @"beauty/:id"**
     * [MGJRouter generateURLWithPattern:MGJ_ROUTE_BEAUTY, @[@13]];
     *
     *
     * **@param** pattern  url pattern 比方 @"beauty/:id"
     * **@param** parameters 一个数组,数量要跟 pattern 里的变量一起
     *
     * **@return**
     */
    + (NSString *)generateURLWithPattern:(NSString *)pattern parameters:(NSArray *)parameters;
    @end
    
3.) 小结:模块调用逻辑图+长处+缺陷
3.1) 模块调用逻辑图:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

3.2) 长处:
  • 运用url-block的计划确实能够处理模块间彼此引证,到达解耦
  • 蘑菇街专门用后台来办理路由url,一致办理
    • 处理了iOSAndroid的途径差异性
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
3.3) 缺陷:
  • 需求有当地列出各个组件里有什么 URL 接口可供调用
    • 蘑菇街做了个后台专门办理
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 每个组件都需求初始化,内存里需求保护url-block映射表,组件多了会有内存问题
    // 注册一个组件(一般在发动的时分去注册)
        [MGJRouter registerURLPattern:@"mgj://detail?id=:id&name=:name" toHandler:^(NSDictionary *dic) {
            NSString * oneId = dic[@"id"];
            NSString * name  = dic[@"name"];
            if (oneId && name) {
                //创立组件,并从字典拿到值
                DetailComposite * detail = [[DetailComposite alloc] init];
                detail.oneId = oneId;
                detail.name  = name;
                // 履行组件的办法
                [detail showComposite];
            }
        }];
        // 外界去调用 履行一个组件
        [MGJRouter openURL:@"mgj://detail?id=5&name=leeDev"];
        // 打印出: showComposite _ id = 5 ; name = leeDev
    
  • 参数无固定格局,也需求有文档办理url入参说明
  • url的参数传递受到限制,只能传递常规的字符串参数,无法传递非常规参数,如UIImageNSData等类型
  • 没有区别本地调用长途调用的状况,尤其是长途调用,会因为url参数受限,导致一些功用受限
  • 组件自身依靠了中间件,且涣散注册使的耦合较多
  • url硬编

4.2 Protocol-Class服务注册绑定办法

1.) 代表结构

针对计划一的问题,蘑菇街又提出了另一种组件化的计划:

  • 便是经过protocol界说服务接口
  • 组件经过protocol接口,来拜访完结接口的模块界说的服务:
    • 具体完结便是把protocolclass做一个映射
    • 一起在内存中保存一张映射表
    • 运用的时分,就经过protocol找到对应的class来获取需求的服务。
2.) 音讯传递的中间件
  • [ModuleManager]
    ModuleManager是担任音讯传递的中间件
  • 音讯传递办法:
    • register Module 注册模块
      • 经过 protocol 提前绑定 一个 模块的完结 class
      • 发布 protocol 给一切模块
    • open Module 翻开模块
      • 经过 protocol 获取 绑定的 class
      • 初始化class对应的组件进行运用
  • 传递音讯的中间件源码API:
     /*
     // 注册
    [ModuleManager registerClass:ClassA forProtocol:ProtocolA]
     //调用
    [ModuleManager classForProtocol:ProtocolA]
     */
    @interface ModuleManager : NSObject
    + (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName;
    + (Class)classForProtocolName:(NSString *)protocolName;
    @end
    @interface ModuleManager()
    @property (nonatomic,strong) NSMutableDictionary * map;
    @end
    @implementation ModuleManager
    - (instancetype)init {
        if (self = [super init]) {
            self.map = [NSMutableDictionary dictionary];
        }
        return self;
    }
    + (ModuleManager *)shareManager {
        static dispatch_once_t onceToken;
        static ModuleManager * router = nil;
        dispatch_once(&onceToken, ^{
            if (router == nil) {
                router = [[ModuleManager alloc] init];
            }
        });
        return router;
    }
    + (void)registerClassName:(NSString *)className forProtocolName:(NSString *)protocolName {
        [self shareManager].map[protocolName] = className;
    }
    + (Class)classForProtocolName:(NSString *)protocolName {
        NSString * className = [self shareManager].map[protocolName];
        return NSClassFromString(className);
    }
    @end
    
3.) 小结:模块调用逻辑图+长处+缺陷
3.1) 模块调用逻辑图

protocol-class模块调用逻辑图:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

规划思维和计划一相似,都是经过给组件加了一层wrapper:

3.2) 长处:
  • 处理了计划一中无法传递非常规参数的问题,使得组件间的调用更为便利
  • 结合计划1+计划2,能够区别本地调用长途调用的状况
  • 经过Protocol能够有用约束组件通讯时的传参个数,参数类型
  • plist映射表保护,代替内存中保护映射表,相对更便利办理
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 体系工作接入ModuleManager,进行办理
    • 体系的一些工作会有通知,比方applicationDidBecomeActive会有对应的UIApplicationDidBecomeActiveNotification
    • 组件假如要做响应的话,只需监听这个体系通知即可
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        NSArray *modules = [[ModuleManager sharedInstance] allModules];
        for (id module in modules) {
            if ([module respondsToSelector:_cmd]) {
                [module application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
            }
        }
    }
    
3.3) 缺陷:
  • 仍然没有处理组件依靠中间件的问题
  • 组件的涣散调用的问题。涣散调用仍然会导致必定的耦合
  • 一切模块,仍然得提前初始化
  • 关于 体系 工作的 办理,需求遍历全部已注册模块,项目体量大时,会有必定的性能损耗
  • 关于 模块 办理 短缺归类 区别,只用一维key-Value联系,过于粗犷

4.3 Target-Action反射办法:依据Runtime+OC反射机制+分类

1.) 代表结构 CTMediator
[CTMediator工程目录]
|
|-[CTMediator]
|        |-CTMediator.h.m
|        |-[Categories]
|                |-[ModuleA]
|                        |-CTMediator+CTMediatorModuleAActions.h.m
|
|-[DemoModule]
|        |-[Actions]
|        |       |-Target_A.h.m
|        |-DemoModuleADetailViewController.h.m
|
|-AppDelegate.h.m
|-ViewController.h.m
2.) 音讯传递的中间件
  • CTMediator
    CTMediator是担任音讯传递的中间件;
  • 音讯传递办法:
    • 1、把需求开放给其它模块的API,封装操作到Target_XXXModule类
    • 2、经过给中间件CTMediator添加分类,凭借分类调用当时模块发布的API
      • 调用的办法是凭借Runtime里边的”Target-Action“反射机制
      • 防止对模块文件的引证
  • 传递音讯的中间件源码API:
    // 长途App调用进口
    - (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion;
    // 本地组件调用进口
    - (id _Nullable )performTarget:(NSString * _Nullable)targetName action:(NSString * _Nullable)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget;
    // 开释 Target
    - (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName;
    
    • 小结:
      • Target+Action+Params办法;
      • url办法(实践上也是【Target+Action+Params办法】,url解析之后就变成【Target+Action+Params】)
      • 回来值 为 id 类型
        • 可能是被调用的Target实例
        • 可能是被调用的nil
        • 可能是被调用的布尔值,代表Target-Action履行的结果
  • DemoModule
    一个例子模块,假设咱们要从其他事务(ViewController.h.m)中跳转到这个事务模块。

在这个demo中,咱们的意图是从其他事务(ViewController.h.m中)跳转到DemoModule事务模块。

3.) 小结:模块调用时序图+长处+缺陷
3.1) 模块调用运转时时序图:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
调用联系概述:

  • 首先由ViewController.h.m建议调用请求给CTMediator
  • CTMediator经过runtime去调用目标模块DemoModule
  • 目标模块DemoModule依据参数创立自己的一个实例,并把这个实例回来给CTMediator
  • CTMediator再把这个实例回来给ViewController.h.m
  • (此刻ViewController.h.m不需求知道这个实例的具体类型,只需求知道是UIViewController的子类),然后由ViewController.h.m决议以什么样的办法去展示DemoModule。

进一步细化DemoModule内部的调用逻辑(过程4、5):

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

每个过程的相关代码:

  • 过程1:
    ViewController.m建议调用请求给CTMediator
    CTMediator经过分类CTMediator+CTMediatorModuleAActions.m露出ModuleA的API
    UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];
    
  • 过程2:
    CTMediator+CTMediatorModuleAActions.m经过界说好的参数调用CTMediator
    //因为CTMediator+CTMediatorModuleAActions是CTMediator的扩展,所以能够直接运用self来调用CTMediator的完结
    UIViewController *viewController =
            [self performTarget:kCTMediatorTargetA
                         action:kCTMediatorActionNativFetchDetailViewController
                         params:@{@"key":@"value"}];
    
  • 过程3:
    CTMediator依据CTMediator+CTMediatorModuleAActions.m传过来的TargetActionparams建议实践调用。
    这个调用联系是经过 OC的运转时机制Runtime 完结的。
    所以此处并不需求在代码上依靠被调用者,假如被调用者不存在,也能够在运转时进行处理。
    return [target performSelector:action withObject:params];
    
  • 过程4/5:
    Target_A创立一个DemoModuleADetailViewController类型的实例(这个实例是Target_A经过DemoModuleADetailViewController类的alloc/init创立的)。
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    
  • 过程6:
    Target_A回来创立的实例到CTMediator.m
    (建议时是经过runtime,过程3),回来后CTMediator.m并不知道这个实例的具体类型,也不会对这个类进行任何解析操作,所以CTMediator.m跟回来的实例之间是没有任何引证联系的。
  • 过程7:
    CTMediator.m回来过程6中得到的实例到CTMediator+CTMediatorModuleAActions.m(建议时是过程2)。
  • 过程8:
    CTMediator+CTMediatorModuleAActions.m会将过程7回来的实例当作UIViewController处理
    接下来会在运转时判别这个实例的类型是不是UIViewController(是不是UIViewController的子类)。
    然后将得到的UIViewController交给调用者ViewController.m(由ViewController.m担任以何种办法进行展示)
3.2) 一切类的功用:
  • CTMediator.h.m功用:

    • 指定目标(target,类名)+动作(action,办法名),并供给一个字典类型的参数。
    • CTMediator.h.m会判别target-action是否能够调用.假如能够,则调用
      • 因为这一功用是经过Runtime动态完结的,所以在CTMediator.h.m的完结中,不会依靠任何其他模块,也不需求知道target-action的具体功用
      • 只需target-action存在,就会被履行(target-action具体的功用由DemoModule自己担任)。
    • CTMediator.h里实践供给了两个办法:
      • 别离处理url办法的调用target-action办法的调用
      • 其间,假如运用url办法,会自动把url转换成target-action。
  • CTMediator+CTMediatorModuleAActions.h.m功用:

    • CTMediator的扩展,用于办理跳转到DemoModule模块的动作。
    • 其他模块想要跳转到DemoModule模块时,经过调用这个类的办法来完结。
    • 但是这个类中,并不真正去做跳转的动作,它仅仅对CTMediator.h.m类的封装,
    • 这样开发者就不需求关心运用CTMediator.h.m跳转到DemoModule模块时具体需求的target称号和action称号了。
    • CTMediator.h.mCTMediator+CTMediatorModuleAActions.h.m一起组成了一个面向DemoModule的跳转,而且它不会在代码上依靠DemoModuleDemoModule是否供给了相应的跳转功用,只体现在运转时是否能够正常跳转。
    • 至此,CTMediator这个中间层完结了完全的独立,其他模块不需求预先注册,CTMediator也不需求知道其他模块的完结细节。
    • 仅有的相关便是需求CTMediator+CTMediatorModuleAActions.h.m中写明正确的targetaction正确的参数
      • 而且这些action和参数只依靠于Target_A.h.m
      • action参数的正确性只会在运转时查看
      • 假如targetaction不存在,能够在CTMediator.h.m中进行相应的处理。
      • 即:CTMediator不需求依靠任何模块就能够编译运转。
  • Target_A.h.m

    • 供给了跳转到DemoModule模块的对外接口
    • CTMediator+CTMediatorModuleAActions.h.m彼此对应,能够说它只用来为CTMediator+CTMediatorModuleAActions.h.m供给服务,所以在完结CTMediator+CTMediatorModuleAActions.h.m时只需求参考Target_A.h.m即可,足够简略以至于并不需求文档来辅佐描绘。
    • 其他模块想跳转到这个模块时,不能直接经过Target_A.h.m完结,而是要经过CTMediator+CTMediatorModuleAActions.h.m来完结。这样,就完结了模块间彼此不依靠,而且只需需求跳转到其他模块的当地,才需求依靠CTMediator
3.3) 长处:
  • 模块经过中间件解析,再由中间件发送音讯通讯
  • 中间件经过 runtime 接口解耦
  • 经过 target-action 简化写法,调用简略便利
  • 代码自动补全编译时查看都仍然有用
  • 经过 category 感官上别离组件接口代码
3.4) 缺陷:
  • Category 存在重名覆盖的危险,需求经过开发标准以及一些查看机制来规避
  • 处理 模块间 彼此 引证 耦合严峻的 问题, 但 一套API 需求必定的重复的封装,对编程提效,不是很友爱
    • 每添加一个模块,就要弥补一个Category完结,一个TargetModule完结,模块多了,这个得想办法恰当削减
    • 模块A 想要调用 模块B 的一套 API,模块B就得自己模块内部完结一遍,且经过必定的办法 露出给 模块A,
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • Mediator 内部存在必定的参数硬编
    • 项目体量大的时分,具有必定的 保护性 难度
    • 对开发功率有必定影响
    • 并不友爱,且写法欠高雅
  • 命名规矩并不美观且相对复杂:每个Target都得加Target_A,每个Action都得加Action_
  • 调用 Mediator API 的 回来值 是什么 无法不清晰知晓,很不友爱,还得编写必定的类型查看代码

4.4 模块服务办理东西:模块服务注册+URL-HandlerBlock路由注册表计划

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

1.) 代表结构 :BeeHive
[BeeHive工程目录]
|
|-[BeeHive]
|        |-BHAnnotation.h.m
|        |-BHModuleProtocol.h
|        |-BHModuleManager.h.m
|        |
|        |-BHRouter.h.m
|        |
|        |-BHServiceProtocol.h
|        |-BHServiceManager.h.m
|        |
|        |-BHAppDelegate.h.m
|        |-BHContext.h.m
|        |-BHConfig.h.m
|        |-BeeHive.h.m
|        |-BeeHive.bundle
|        |      |  - BeeHive.plist
|        |      |  - BHService.plist
|        |
|        |-BHTimeProfiler.h.m
|        |-BHWatchDog.h.m
|        |
|        |- BHDefines.h
|        |- BHCommon.h
|        |
2.) 音讯传递的中间件
  • BeeHive
    • BHModuleManager是担任 本运用内音讯传递 的中间件;
    • BHRouter是担任 跨运用服务音讯传递 的中间件;
  • 音讯传递办法:
    经过处理Event编写各个事务模块能够完结插件化编程
    各事务模块之间没有任何依靠,coremodule之间经过event交互,完结了插件阻隔。
    1. 依据接口的完结Service拜访办法(Java spring结构完结)
      • BHModuleManager
        • 音讯分类+模块音讯界说+模块注册+模块调用
          • BeeHive模块办理东西把工作区别为三类进行办理:
            体系级工作运用级工作事务自界说工作
            • 体系工作通常是Application生命周期工作
            • 体系工作的基础之上,扩展了运用的通用工作
              例如modSetupmodInit等,能够用于编码完结各插件模块的设置与初始化
            • 假如觉得体系工作通用工作不足以满意需求,能够经过承继BHAppdelegate来扩展当时运用的事务自界说工作BHAppdelgate内原本就封装了对体系工作、通用工作的处理)
          • 模块注册之后就能够 给 其它模块 进行调用
          • 能够经过给 模块办理中间件BHModuleManager 发送 另一个模块的音讯调用另一个模块
          • 也能够经过 [[BeeHive shareInstance] createService:@protocol(XXXServiceProtocol)] 直接获取一个服务
    2. 依据跨运用完结的URL Route办法(iPhoneApp之间的互访)
      • BHRouter 相似于 蘑菇街 的URL-HandlerBlock计划
      • 支撑App间互访,也支撑Hybrid开发时,经过训练拜访web事务包(相似于小程序技能)
2.1) 了解其结构规划

1、完结特性

  • 插件化的模块开发运转结构
  • 模块具体完结与接口调用别离
  • 模块生命周期办理,扩展了运用的体系工作

2、规划准则

  • 因为依据SpringService理念,尽管能够使模块间的具体完结与接口解耦,但无法防止对接口类的依靠联系。
  • 为什么不运用invoke以及动态链接库技能完结对接口完结的解耦,相似ApacheDSO的办法?
    • 主要是考虑学习本钱难度
    • 以及动态调用完结无法在编译查看阶段检测接口参数变更等问题
    • 动态技能需求更高的编程门槛要求。

3、项目名来源

  • BeeHive灵感来源于蜂窝
  • 蜂窝是世界上高度模块化的工程结构,六边形的规划能带来无限扩张的可能
  • 所以作者用了BeeHive来做为这个项意图命名

4、模块生命周期的工作

BeeHive会给每个模块供给生命周期工作,用于与BeeHive宿主环境进行必要信息交互,感知模块生命周期的改变。

2.2)了解结构工作分层

工作分为三种类型:

  • 体系工作
  • 通用工作
  • 事务自界说工作

1、体系工作

体系工作通常是Application生命周期工作,例如DidBecomeActiveWillEnterBackground等。

体系工作根本作业流如下:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

2、通用工作

在体系工作的基础之上,扩展了运用的通用工作,例如modSetupmodInit等,能够用于编码完结各插件模块的设置与初始化。

扩展的通用工作如下:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

3、事务自界说工作

假如觉得体系工作、通用工作不足以满意需求,作者还将工作封装简化成BHAppdelgate,你能够经过承继 BHAppdelegate来扩展自己的工作。

2.3) 模块注册办法

模块注册的办法有静态注册与动态注册两种。

1、 静态注册

经过在BeeHive.plist文件中注册符合BHModuleProtocol协议模块类:

02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】

2、 动态注册

@implementation HomeModule
BH_EXPORT_MODULE() // 声明该类为模块进口
@end

在模块进口类完结中 运用BH_EXPORT_MODULE()宏声明该类为模块进口完结类。

3、异步加载模块

假如设置模块导出为BH_EXPORT_MODULE(YES),则会在发动之后第一屏内容展示之前异步履行模块的初始化,能够优化发动时时间消耗。

2.4) 编程开发运用

BHModuleProtocol为各个模块供给了每个模块能够Hook的函数,用于完结插件逻辑以及代码完结。

1、设置环境变量

经过context.env能够判别咱们的运用环境状况来决议咱们怎么装备咱们的运用。

-(void)modSetup:(BHContext *)context{
    switch (context.env) {
        case BHEnvironmentDev:
        //....初始化开发环境
        break;
        case BHEnvironmentProd:
        //....初始化出产环境
        default:
        break;
    }
}

2、模块初始化

假如模块有需求发动时初始化的逻辑,能够在modInit里编写,例如模块注册一个外部模块能够拜访的Service接口

-(void)modInit:(BHContext *)context {
    //注册模块的接口服务
    [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
}

3、处理体系工作

体系的工作会被传递给每个模块,让每个模块自己决议编写事务处理逻辑,比方3D-Touch功用

-(void)modQuickAction:(BHContext *)context{
    [self process:context.shortcutItem handler:context.scompletionHandler];
}

4、模间调用

经过处理Event编写各个事务模块能够完结插件化编程,各事务模块之间没有任何依靠,coremodule之间经过event交互,完结了插件阻隔。但有时分咱们需求模块间的彼此调用某些功用来协同完结功用。

结构供给了两种办法的接口拜访办法:

  1. 依据接口的完结Service拜访办法(Java spring结构完结)
  2. 依据跨运用完结的URL Route办法(iPhone App之间的互访)

5、 界说接口

以为HomeServiceProtocol为例。

@protocol HomeServiceProtocol <NSObject, BHServiceProtocol>
- (void)registerViewController:(UIViewController *)vc title:(NSString *)title iconName:(NSString *)iconName;
@end

6、注册Service

有两种办法:

  • API注册
    [[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];
    
  • BHService.plist注册
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>HomeServiceProtocol</key>
        <string>BHViewController</string>
    </dict>
    </plist>
    

7、调用Service

#import "BHService.h"
id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

8、单例与多例

关于有些场景下,咱们拜访每个声明Service的目标,期望目标能保留一些状况,那咱们需求声明这个Service目标是一个单例目标。

咱们只需求在Service目标中完结工作函数声明

-(BOOL) singleton{
    return YES;
}

经过createService获取的目标则为单例目标,假如完结上面函数回来的是NO,则createService回来的是多例。

id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];

9、上下文环境Context

  • 初始化设置运用的项目信息,并在各模块间同享整个运用程序的信息
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [BHContext shareInstance].env = BHEnvironmentDev; //界说运用的运转开发环境
        [BHContext shareInstance].application = application;
        [BHContext shareInstance].launchOptions = launchOptions;
        [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/CustomModulePlist";//可选,默以为BeeHive.bundle/BeeHive.plist
        [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/CustomServicePlist";//可选,默以为BeeHive.bundle/BHService.plist
        [[BeeHive shareInstance] setContext:[BHContext shareInstance]];
        [super application:application didFinishLaunchingWithOptions:launchOptions];
        id<HomeServiceProtocol> homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
        if ([homeVc isKindOfClass:[UIViewController class]]) {
            UINavigationController *navCtrl = [[UINavigationController alloc] initWithRootViewController:(UIViewController*)homeVc];
            self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
            self.window.rootViewController = navCtrl;
            [self.window makeKeyAndVisible];
        }
        return YES;
    }
    
3.) 小结:作业流图+架构图+核心类联系图+长处+缺陷
3.1) 作业流图+架构图+核心类联系图
  • 体系工作根本作业流:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 扩展的通用工作+体系工作根本作业流:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 根本架构图:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • BeeHive具体架构图:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 核心类联系图:
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
3.2) 一切类的功用如下:
  • BHModuleManager
    • 担任一切工作(体系级工作运用级工作事务自界说工作)的音讯传递
    • BHModuleManager对外发布的API 如下:
      //模型等级,用于分层规划
      typedef NS_ENUM(NSUInteger, BHModuleLevel){
          BHModuleBasic = 0,
          BHModuleNormal = 1
      };
      typedef NS_ENUM(NSInteger, BHModuleEventType){
          /// #pragma mark - 1-通用运用级工作
          BHMSetupEvent = 0,
          BHMInitEvent,
          BHMTearDownEvent,
          BHMSplashEvent,
          /// #pragma mark - 2-体系级工作
          BHMQuickActionEvent,
          BHMWillResignActiveEvent,
          BHMDidEnterBackgroundEvent,
          BHMWillEnterForegroundEvent,
          BHMDidBecomeActiveEvent,
          BHMWillTerminateEvent,
          BHMUnmountEvent,
          // 运用交互
          BHMOpenURLEvent,
          // 内存警告
          BHMDidReceiveMemoryWarningEvent,
          // 本地推送、长途推送
          BHMDidFailToRegisterForRemoteNotificationsEvent,
          BHMDidRegisterForRemoteNotificationsEvent,
          BHMDidReceiveRemoteNotificationEvent,
          BHMDidReceiveLocalNotificationEvent,
          BHMWillPresentNotificationEvent,
          BHMDidReceiveNotificationResponseEvent,
          // Widget工作
          BHMWillContinueUserActivityEvent,
          BHMContinueUserActivityEvent,
          BHMDidFailToContinueUserActivityEvent,
          BHMDidUpdateUserActivityEvent,
          // 手表工作
          BHMHandleWatchKitExtensionRequestEvent,
          /// #pragma mark - 3-事务自界说工作
          BHMDidCustomEvent = 1000
      };
      @class BHModule;
      @interface BHModuleManager : NSObject
      + (instancetype)sharedManager;
      // If you do not comply with set Level protocol, the default Normal
      - (void)registerDynamicModule:(Class)moduleClass;
      - (void)registerDynamicModule:(Class)moduleClass
             shouldTriggerInitEvent:(BOOL)shouldTriggerInitEvent;
      - (void)unRegisterDynamicModule:(Class)moduleClass;
      - (void)loadLocalModules;
      - (void)registedAllModules;
      - (void)registerCustomEvent:(NSInteger)eventType
               withModuleInstance:(id)moduleInstance
                   andSelectorStr:(NSString *)selectorStr;
      - (void)triggerEvent:(NSInteger)eventType;
      - (void)triggerEvent:(NSInteger)eventType 
           withCustomParam:(NSDictionary *)customParam;
      @end
      
    • BHModuleManager经过调用triggerEventAPI来处理一切的音讯工作传递:
      - (void)triggerEvent:(BHModuleEventType)eventType {
          switch (eventType) {
              case BHMSetupEvent:
                  [self handleModuleEvent:kSetupSelector];
                  break;
              case BHMInitEvent:
                  //special
                  [self handleModulesInitEvent];
                  break;
              case BHMTearDownEvent:
                  //special
                  [self handleModulesTearDownEvent];
                  break;
              case BHMSplashEvent:
                  [self handleModuleEvent:kSplashSeletor];
                  break;
              case BHMWillResignActiveEvent:
                  [self handleModuleEvent:kWillResignActiveSelector];
                  break;
              case BHMDidEnterBackgroundEvent:
                  [self handleModuleEvent:kDidEnterBackgroundSelector];
                  break;
              case BHMWillEnterForegroundEvent:
                  [self handleModuleEvent:kWillEnterForegroundSelector];
                  break;
              case BHMDidBecomeActiveEvent:
                  [self handleModuleEvent:kDidBecomeActiveSelector];
                  break;
              case BHMWillTerminateEvent:
                  [self handleModuleEvent:kWillTerminateSelector];
                  break;
              case BHMUnmountEvent:
                  [self handleModuleEvent:kUnmountEventSelector];
                  break;
              case BHMOpenURLEvent:
                  [self handleModuleEvent:kOpenURLSelector];
                  break;
              case BHMDidReceiveMemoryWarningEvent:
                  [self handleModuleEvent:kDidReceiveMemoryWarningSelector];
                  break;
              case BHMDidReceiveRemoteNotificationEvent:
                  [self handleModuleEvent:kDidReceiveRemoteNotificationsSelector];
                  break;
              case BHMDidFailToRegisterForRemoteNotificationsEvent:
                  [self handleModuleEvent:kFailToRegisterForRemoteNotificationsSelector];
                  break;
              case BHMDidRegisterForRemoteNotificationsEvent:
                  [self handleModuleEvent:kDidRegisterForRemoteNotificationsSelector];
                  break;
              case BHMDidReceiveLocalNotificationEvent:
                  [self handleModuleEvent:kDidReceiveLocalNotificationsSelector];
                  break;
              case BHMWillContinueUserActivityEvent:
                  [self handleModuleEvent:kWillContinueUserActivitySelector];
                  break;
              case BHMContinueUserActivityEvent:
                  [self handleModuleEvent:kContinueUserActivitySelector];
                  break;
              case BHMDidFailToContinueUserActivityEvent:
                  [self handleModuleEvent:kFailToContinueUserActivitySelector];
                  break;
              case BHMDidUpdateUserActivityEvent:
                  [self handleModuleEvent:kDidUpdateContinueUserActivitySelector];
                  break;
              case BHMQuickActionEvent:
                  [self handleModuleEvent:kQuickActionSelector];
                  break;
              default:
                  [BHContext shareInstance].customEvent = eventType;
                  [self handleModuleEvent:kAppCustomSelector];
                  break;
          }
      }
      
    • 1.1 体系工作办理:
      • BHAppDelegate
        引进BHAppDelegate接收 体系工作
        把原本体系工作回调监听(也便是原本Appdelegate遵守的UIApplicationDelegate协议办法)纳入模块工作办理:
      • 体系工作通常是Application生命周期工作,例如DidBecomeActiveWillEnterBackground
      • 体系工作参数都被当作环境变量缓存了起来,一切模块同享
      • 体系工作根本作业流如下:
        02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
      • 体系工作音讯传递源码:
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMSetupEvent];
            [[BHModuleManager sharedManager] triggerEvent:BHMInitEvent];
            dispatch_async(dispatch_get_main_queue(), ^{
                [[BHModuleManager sharedManager] triggerEvent:BHMSplashEvent];
            });
        #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
            if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0f) {
                [UNUserNotificationCenter currentNotificationCenter].delegate = self;
            }
        #endif
        #ifdef DEBUG
            [[BHTimeProfiler sharedTimeProfiler] saveTimeProfileDataIntoFile:@"BeeHiveTimeProfiler"];
        #endif
            return YES;
        }
        #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400 
        -(void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
        {
            [[BeeHive shareInstance].context.touchShortcutItem setShortcutItem: shortcutItem];
            [[BeeHive shareInstance].context.touchShortcutItem setScompletionHandler: completionHandler];
            [[BHModuleManager sharedManager] triggerEvent:BHMQuickActionEvent];
        }
        #endif
        - (void)applicationWillResignActive:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMWillResignActiveEvent];
        }
        - (void)applicationDidEnterBackground:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMDidEnterBackgroundEvent];
        }
        - (void)applicationWillEnterForeground:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMWillEnterForegroundEvent];
        }
        - (void)applicationDidBecomeActive:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMDidBecomeActiveEvent];
        }
        - (void)applicationWillTerminate:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMWillTerminateEvent];
        }
        - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
        {
            [[BeeHive shareInstance].context.openURLItem setOpenURL:url];
            [[BeeHive shareInstance].context.openURLItem setSourceApplication:sourceApplication];
            [[BeeHive shareInstance].context.openURLItem setAnnotation:annotation];
            [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent];
            return YES;
        }
        #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400
        - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
        {
            [[BeeHive shareInstance].context.openURLItem setOpenURL:url];
            [[BeeHive shareInstance].context.openURLItem setOptions:options];
            [[BHModuleManager sharedManager] triggerEvent:BHMOpenURLEvent];
            return YES;
        }
        #endif
        - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
        {
            [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveMemoryWarningEvent];
        }
        - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
        {
            [[BeeHive shareInstance].context.notificationsItem setNotificationsError:error];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToRegisterForRemoteNotificationsEvent];
        }
        - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
        {
            [[BeeHive shareInstance].context.notificationsItem setDeviceToken: deviceToken];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidRegisterForRemoteNotificationsEvent];
        }
        - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
        {
            [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent];
        }
        - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
        {
            [[BeeHive shareInstance].context.notificationsItem setUserInfo: userInfo];
            [[BeeHive shareInstance].context.notificationsItem setNotificationResultHander: completionHandler];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveRemoteNotificationEvent];
        }
        - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
        {
            [[BeeHive shareInstance].context.notificationsItem setLocalNotification: notification];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveLocalNotificationEvent];
        }
        #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80000
        - (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity
        {
            if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){
                [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity];
                [[BHModuleManager sharedManager] triggerEvent:BHMDidUpdateUserActivityEvent];
            }
        }
        - (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error
        {
            if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){
                [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType];
                [[BeeHive shareInstance].context.userActivityItem setUserActivityError: error];
                [[BHModuleManager sharedManager] triggerEvent:BHMDidFailToContinueUserActivityEvent];
            }
        }
        - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
        {
            if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){
                [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity];
                [[BeeHive shareInstance].context.userActivityItem setRestorationHandler: restorationHandler];
                [[BHModuleManager sharedManager] triggerEvent:BHMContinueUserActivityEvent];
            }
            return YES;
        }
        - (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType
        {
            if([UIDevice currentDevice].systemVersion.floatValue > 8.0f){
                [[BeeHive shareInstance].context.userActivityItem setUserActivityType: userActivityType];
                [[BHModuleManager sharedManager] triggerEvent:BHMWillContinueUserActivityEvent];
            }
            return YES;
        }
        #endif
        #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
        - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
            [[BeeHive shareInstance].context.notificationsItem setNotification: notification];
            [[BeeHive shareInstance].context.notificationsItem setNotificationPresentationOptionsHandler: completionHandler];
            [[BeeHive shareInstance].context.notificationsItem setCenter:center];
            [[BHModuleManager sharedManager] triggerEvent:BHMWillPresentNotificationEvent];
        };
        - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
            [[BeeHive shareInstance].context.notificationsItem setNotificationResponse: response];
            [[BeeHive shareInstance].context.notificationsItem setNotificationCompletionHandler:completionHandler];
            [[BeeHive shareInstance].context.notificationsItem setCenter:center];
            [[BHModuleManager sharedManager] triggerEvent:BHMDidReceiveNotificationResponseEvent];
        };
        #endif
        
      • 体系工作参数都存在BHContext的公开特点列表
  • 1.2 通用运用级工作:
    在体系工作的基础之上,扩展了运用的通用工作,例如modSetupmodInitmodSplash,能够用于编码完结各插件模块的设置与初始化
    • 扩展的通用工作如下:
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • 1.3 事务自界说工作:
    • 假如体系工作通用工作仍不满意运用完结需求,咱们能够添加事务自界说工作
    • 咱们能够经过承继BHAppdelegate来扩展自己的工作,在咱们自己的Appdelegate注册更多的工作。(关于模块的注册,咱们在后面再进一步深化了解)
    • 自界说的工作的type值便是BHMDidCustomEvent >= 1000
    • BHModuleManager内有一个API专门处理事务自界说工作
      - (void)registerCustomEvent:(NSInteger)eventType
         withModuleInstance:(id)moduleInstance
             andSelectorStr:(NSString *)selectorStr {
          if (eventType < 1000) {
              return;
          }
          [self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr];
      }
      
  • BHModuleManager中有2个特殊工作:
    • BHMInitEvent工作: 初始化Module模块的工作,关键代码
      - (void)handleModulesInitEventForTarget:(id<BHModuleProtocol>)target
      withCustomParam:(NSDictionary *)customParam {
          BHContext *context = [BHContext shareInstance].copy;】
          context.customParam = customParam;
          context.customEvent = BHMInitEvent;
          NSArray<id<BHModuleProtocol>> *moduleInstances;
          if (target) {
              moduleInstances = @[target];
          } else {
              moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMInitEvent)];
          }
          [moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
                  __weak typeof(&*self) wself = self;
                  void ( ^ bk )(void);
                  bk = ^(){
                      __strong typeof(&*self) sself = wself;
                      if (sself) {
                          if ([moduleInstance respondsToSelector:@selector(modInit:)]) {
                              [moduleInstance modInit:context];
                          }
                      }
                  };
                  [[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- modInit:", [moduleInstance class]]];
                  if ([moduleInstance respondsToSelector:@selector(async)]) {
                      BOOL async = [moduleInstance async];
                      if (async) {
                          dispatch_async(dispatch_get_main_queue(),^{
                              bk();
                          });
                      } else {
                          bk();
                      } 
                  } else { 
                      bk();
                  } 
          }];
      }
      
      • 遍历BHModules数组
      • 顺次对每个Module实例调用modInit:办法;
      • 这里会有异步加载的问题:
      • 假如moduleInstance重写了async办法,那么就会依据这个办法回来的值来进行是否异步加载的判别
      • 默许不异步
      • modInit:办法里边干许多工作:
        • 比方说对环境的判别,依据环境的不同初始化不同的办法:
          -(void)modInit:(BHContext *)context {
              switch (context.env) {
                  case BHEnvironmentDev:
                      //....初始化开发环境
                      break;
                  case BHEnvironmentProd:
                      //....初始化出产环境
                  default:
                      break;
              }
          }
          
        • 再比方,注册一个事务服务初始化模块的某事务服务,给其传参:
          -(void)modInit:(BHContext *)context {
              NSLog(@"模块初始化中"); 
              NSLog(@"%@",context.moduleConfigName);
              // [[BeeHive shareInstance] registerService:@protocol(UserTrackServiceProtocol) service:[BHUserTrackViewController class]];
              id<TradeServiceProtocol> service = [[BeeHive shareInstance] createService:@protocol(TradeServiceProtocol)];
              service.itemId = @"我是单例";
          }
          
    • BHMTearDownEvent工作:卸载Module模块的工作,关键代码:
      - (void)handleModulesTearDownEventForTarget:(id<BHModuleProtocol>)target
          withCustomParam:(NSDictionary *)customParam {
              BHContext *context = [BHContext shareInstance].copy;
              context.customParam = customParam;
              context.customEvent = BHMTearDownEvent;
              NSArray<id<BHModuleProtocol>> *moduleInstances;
              if (target) {
                  moduleInstances = @[target];
              } else {
                  moduleInstances = [self.BHModulesByEvent objectForKey:@(BHMTearDownEvent)];
              }
              //Reverse Order to unload
              for (int i = (int)moduleInstances.count - 1; i >= 0; i--) {
                  id<BHModuleProtocol> moduleInstance = [moduleInstances objectAtIndex:i];
                  if (moduleInstance && [moduleInstance respondsToSelector:@selector(modTearDown:)]) {
                      [moduleInstance modTearDown:context];
                  }
              }
      }
      
    • 因为Module是有优先级Level,所以撤除的时分需求从低优先级开端拆
      • 即数组逆序循环
      • 对每个Module实例发送modTearDown:工作即可
  • BHServiceManager:BeeHive内部专门用于办理服务(注册反注册查询)的东西类
    • 核心API:
      #import <Foundation/Foundation.h>
      @class BHContext;
      @interface BHServiceManager : NSObject
      @property (nonatomic, assign) BOOL enableException;
      + (instancetype)sharedManager;
      - (void)registerLocalServices;
      - (void)registerService:(Protocol *)service implClass:(Class)implClass;
      - (id)createService:(Protocol *)service;
      - (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName;
      - (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache;
      - (id)getServiceInstanceFromServiceName:(NSString *)serviceName;
      - (void)removeServiceWithServiceName:(NSString *)serviceName; 
      @end
      
  • BHRouter: 是一个专门担任处理URL-HandlerBlock长途 APP工作的 插件
    • 对外发布的API和相应的介绍如下:
      // 协议头Key
      static NSString *const BHRURLSchemeGlobalKey = @"URLGlobalScheme";
      // 【路由表】工作分类:
      static NSString *const BHRURLHostCallService = @"call.service.beehive";//调用服务
      static NSString *const BHRURLHostRegister = @"register.beehive";//注册服务
      static NSString *const BHRURLHostJumpViewController = @"jump.vc.beehive";//跳转页面
      //【路由URL规矩】:
      static NSString *const BHRURLSubPathSplitPattern = @".";//url途径分节点的标识
      static NSString *const BHRURLQueryParamsKey = @"params";//url途径传参Key
      static NSString *const BHRURLFragmentViewControlerEnterModePush = @"push";//页面翻开的办法Push
      static NSString *const BHRURLFragmentViewControlerEnterModeModal = @"modal"; //页面翻开的办法Model
      typedef void(^BHRPathComponentCustomHandler)(NSDictionary<NSString *, id> *params);//HandlerBlock的类型界说
      @interface BHRouter : NSObject
      - (instancetype)init NS_UNAVAILABLE;
      + (instancetype)new NS_UNAVAILABLE;
      + (instancetype)globalRouter;//模块办理东西默许协议头
      + (instancetype)routerForScheme:(NSString *)scheme;//新增一个协议头对应的路由表
      + (void)unRegisterRouterForScheme:(NSString *)scheme;//开释一个某协议头的路由表
      + (void)unRegisterAllRouters;//开释一切缓存好的路由表
      //handler is a custom module or service solve function
      - (void)addPathComponent:(NSString *)pathComponentKey
         forClass:(Class)mClass;
      - (void)addPathComponent:(NSString *)pathComponentKey
         forClass:(Class)mClass
          handler:(BHRPathComponentCustomHandler)handler;
      - (void)removePathComponent:(NSString *)pathComponentKey;
      //url - > com.alibaba.beehive://call.service.beehive/pathComponentKey.protocolName.selector/...?params={}(value url encode)
      //url - > com.alibaba.beehive://register.beehive/pathComponentKey.protocolName/...?params={}(value url encode)
      //url - > com.alibaba.beehive://jump.vc.beehive/pathComponentKey.protocolName.push(modal)/...?params={}(value url encode)#push
      //params -> {pathComponentKey:{paramName:paramValue,...},...}
      //when call service, paramName = @1,@2,...(order of paramValue)
      + (BOOL)canOpenURL:(NSURL *)URL;
      + (BOOL)openURL:(NSURL *)URL;
      + (BOOL)openURL:(NSURL *)URL
       withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params;
      + (BOOL)openURL:(NSURL *)URL
       withParams:(NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)params
          andThen:(void(^)(NSString *pathComponentKey, id obj, id returnValue))then;
      @end
      
    • BHRouter 支撑依据一个新的协议头,新增一个路由表 , 这是开发者 考虑到了 一套代码多套运用的场景!
      • 商家端买家端 一体的 电商
      • 内部职工版别 与 To C版别 一体的 运用
      • To B版别 与 To C版别 一体的 运用
      • 互联网金融银行银行ToC端,有时分会依据不同的地级市也有不同的版别,多套运用一体,可随时切换
    • BHRouter 与 蘑菇街 的 URL-HandlerBlock 不同的当地:
      • BeeHive把App内部工作和 跨运用工作 的 治理 分开了;
      • BHRouter专门担任 长途工作跨运用工作;
  • BHContext:单例
    imp文件内部有两个字典,用于存储模块信息(modulesByName)和服务信息(servicesByName)
    02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • 发布一些存储环境变量的特点和和对服务办理三个API如下:
      #import <Foundation/Foundation.h>
      #import "BHServiceProtocol.h"
      #import "BHConfig.h"
      #import "BHAppDelegate.h"
      typedef enum{  
      BHEnvironmentDev = 0,
      BHEnvironmentTest,
      BHEnvironmentStage,
      BHEnvironmentProd
      }BHEnvironmentType;
      @interface BHContext : NSObject <NSCopying>
      //global env
      @property(nonatomic, assign) BHEnvironmentType env;
      //global config
      @property(nonatomic, strong) BHConfig *config;
      //application appkey
      @property(nonatomic, strong) NSString *appkey;
      //customEvent>=1000
      @property(nonatomic, assign) NSInteger customEvent;
      @property(nonatomic, strong) UIApplication *application;
      @property(nonatomic, strong) NSDictionary *launchOptions;
      @property(nonatomic, strong) NSString *moduleConfigName;
      @property(nonatomic, strong) NSString *serviceConfigName;
      //3D-Touch model
      #if __IPHONE_OS_VERSION_MAX_ALLOWED > 80400
      @property (nonatomic, strong) BHShortcutItem *touchShortcutItem;
      #endif
      //OpenURL model
      @property (nonatomic, strong) BHOpenURLItem *openURLItem;
      //Notifications Remote or Local
      @property (nonatomic, strong) BHNotificationsItem *notificationsItem;
      //user Activity Model
      @property (nonatomic, strong) BHUserActivityItem *userActivityItem;
      //watch Model
      @property (nonatomic, strong) BHWatchItem *watchItem;
      //custom param
      @property (nonatomic, copy) NSDictionary *customParam;
      + (instancetype)shareInstance;
      - (void)addServiceWithImplInstance:(id)implInstance serviceName:(NSString *)serviceName;
      - (void)removeServiceWithServiceName:(NSString *)serviceName;
      - (id)getServiceInstanceFromServiceName:(NSString *)serviceName;
      @end
      
    • 运用发动的时分,能够初始化一些上下文信息:
      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
      [BHContext shareInstance].application = application;
      [BHContext shareInstance].launchOptions = launchOptions;
      [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";//可选,默以为BeeHive.bundle/BeeHive.plist
      [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";
      [BeeHive shareInstance].enableException = YES;
      [[BeeHive shareInstance] setContext:[BHContext shareInstance]];
      [[BHTimeProfiler sharedTimeProfiler] recordEventTime:@"BeeHive::super start launch"];
      [super application:application didFinishLaunchingWithOptions:launchOptions];
      ...
      return YES;
      }
      
  • BHConfig:单例
    保存了一个config的NSMutableDictionary字典
    字典责任是保护一些动态的环境变量,作为BHContext的弥补存在;API:
    #import <Foundation/Foundation.h>
    @interface BHConfig : NSObject
    + (instancetype)shareInstance;
    + (id)get:(NSString *)key;
    + (BOOL)has:(NSString *)key;
    + (void)add:(NSDictionary *)parameters;
    + (NSMutableDictionary *)getAll;
    + (NSString *)stringValue:(NSString *)key;
    + (NSDictionary *)dictionaryValue:(NSString *)key;
    + (NSInteger)integerValue:(NSString *)key;
    + (float)floatValue:(NSString *)key;
    + (BOOL)boolValue:(NSString *)key;
    + (NSArray *)arrayValue:(NSString *)key;
    + (void)set:(NSString *)key value:(id)value;
    + (void)set:(NSString *)key boolValue:(BOOL)value;
    + (void)set:(NSString *)key integerValue:(NSInteger)value;
    + (void)clear;
    @end
    
  • BeeHive :结构作者用这个类来发布结构一切的插件API
    • BeeHive里边也有一个triggerCustomEvent:办法便是用来处理这些工作的,尤其是处理自界说工作的:
      + (void)triggerCustomEvent:(NSInteger)eventType {
      if(eventType < 1000) { 
      return; 
      } 
      [[BHModuleManager sharedManager] triggerEvent:eventType];
      }
      
    • BeeHive公开的API:
      #import <Foundation/Foundation.h>
      #import "BHModuleProtocol.h"
      #import "BHContext.h"
      #import "BHAppDelegate.h"
      #import "BHModuleManager.h"
      #import "BHServiceManager.h"
      @interface BeeHive : NSObject
      //save application global context
      @property(nonatomic, strong) BHContext *context;
      @property (nonatomic, assign) BOOL enableException;
      + (instancetype)shareInstance;
      + (void)registerDynamicModule:(Class) moduleClass;
      - (id)createService:(Protocol *)proto;
      //Registration is recommended to use a static way
      - (void)registerService:(Protocol *)proto service:(Class) serviceClass;
      + (void)triggerCustomEvent:(NSInteger)eventType;
      @end
      
    • BeeHive设置 相关着全局环境参数 的上下文目标的时分,会默许加载本地服务列表中一切的服务和模块
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
  • BeeHive.bundle
    bundle内部存储两个plist:
    • BeeHive.plist :
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • moduleClasses 存储的是 一组模块 信息,每个模块信息都用一个字段存储。字段注解:
      • moduleClass : 模块类名
      • moduleLevel : 模块工作等级(Integer值,值越大。模块分层的辅佐值)
      • modulePriority: 模块工作优先级(Integer值,值越大。同级模块下,分工作的优先级的辅佐值)
        02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
    • BHService.plist
      02-iOS架构设计 | iOS模块化开发 【模块的三级分类、模块划分策略、几种模块化处理方案、模块与模块管理设计】
      • service : 供给的服务露出的接口文件(协议名)
      • impl : 服务的具体完结类类名
  • BHTimeProfiler:
    BHTimeProfiler便是用来进行核算时间性能方面的Profiler
  • BHWatchDog:
    能够开一个线程,设置好handler,每隔一段时间就履行一个handler
3.3) 长处:
  • MGJRouterCTMediatorBeeHive 三者间,归于最优异的结构
  • 在模块化开发中,用Context办理环境变量,全局信息同享的计划优于用一个Constant文件办理
  • 依据接口Service拜访,长处是能够编译时查看发现接口的变更,然后及时修正接口问题
    • 对模块办理进行归类办理,进步了工作的有条理性、可读性、可保护性
      • 模块办理分Level层级
      • 同级 模块 分 优先级
      • 经过 静态文件 plist 办理,添加了可保护性
      • 凭借中间件,经过event的办法分发工作音讯,模块办理的接口规矩且一致
  • 依据接口URL Route拜访
    • 支撑 添加 映射表,为事务模块进一步分级供给了可能性
    • 支撑 添加 映射表,为单进程多运用开发供给了快捷性
    • URL Route 内部 完结,可拜访 模块服务
    • URL Route 完结,也用了Target-Action
  • 模块注册办法有两种
    • 静态注册,更倾向于 运用开发 时运用,为运用保护供给便利性
    • 动态注册,更倾向于 SDK 开发 时运用,为代码封装供给便利性
  • 接口简练,易上手
3.4) 缺陷:
  • 依据接口Service拜访,缺陷是需求依靠接口界说的头文件,经过模块添加得越多,保护接口界说也有必定作业量
  • BeeHive模块办理功用全面,适合有必定体量的大运用项目。关于小项目仍是太重了,能够进一步拆分子库
    • 针对SDK开发区别
    • 针对小项目拆分
    • 针对大项目,选取结构其部分功用,作为代替计划拆分
  • 针对不同模块的分级办理不行清晰,能够进一步区别办理

四、模块与模块办理

至此,关于模块与模块办理功用。咱们结合前面的剖析与介绍,咱们能够做一下小结:

1. 运用项目、SDK等的模块区别

针对大项目:

  • 进行模块三级分类: 三级模块+组件+插件``运用级模块事务级模块功用级模块
    • 运用级模块 = 事务级模块1 + 事务级模块2 + 事务级模块3……
    • 事务级模块 = 若干功用级模块 + 若干UI组件
  • 模块内部能够进一步区别组件:
    • 功用级模块 = 功用组件A+功用组件B+功用组件C

针对小项目:

  • 在小项目中【模块与组件】的概念往往混合运用,不怎么区别
  • 模块区别的颗粒度不必太详尽,适宜即可; 参考:二级模块+组件+插件
    • 如: 运用级模块事务级模块功用组件UI组件第三方社会化共享插件

针对SDK:

在SDK/功用模块Pod库内部区别往往是按一级模块/二级模块区别处理的:

  1. 功用模块 = 若干功用插件+若干功用组件+若干UI组件
  2. 事务服务模块 = 若干功用模块+若干事务UI组件

2.模块的集成依靠:

  • 壳工程集成运用级模块
  • 运用级模块 依靠 事务级模块
  • 事务级模块 依靠 功用模块组
  • 功用模块 依靠 功用组件 + UI组件

3.模块的解耦开发与模块间的通讯办理:

  • 工作分类:
    • 体系工作
    • 运用事务工作
    • 自界说工作
  • 由模块办理者中间件作为模块间音讯通讯的枢纽
    模块间通讯的办法:
    • 1.URL+Router
      • 担任 跨运用 服务 的 模块通讯
      • 担任 Hybrid计划 的 本地H5 事务包 模块 的通讯(相似于微信的小程序)
      • 担任 Hybrid计划 的 长途Web 事务页面 模块 的通讯
    • 2.依据接口的完结Service拜访

4.区别模块办理东西小功用模块

(为了便利组合,使其在适应【小项目、大项目、SDK开发】时,导入的办理东西不那么重)

  • 通讯的办法区别为小模块
  • 工作办理区别
  • 注册办法区别
  • 添加模块的办理层级

5.依据 阿里的 BeeHive 进行二次开发

在做这个模块办理东西的时分,我挑选 依据 阿里的 BeeHive 进行二次开发。原因如下:

  • 功用相对完善且安稳:
    阿里的模块办理东西现已历过若干个大项意图检验,功用相对完善且安稳
    • 天猫、淘宝、付出宝等若干
  • 下降学习本钱:
    许多开发者在比较 探求 组件化开发的过程中,现已对BeeHive这个东西相对了解,
  • 结构的姓名:
    一个恰切的姓名,便是一个恰切的界说。
    关于结构的准确界说,是一个优异的插件成功的重要因素
    • BeeHive这个称号把模块的拔插描绘的恰到好处:
      • BeeHive灵感来源于蜂窝
      • 蜂窝是世界上高度模块化的工程结构,六边形的规划能带来无限扩张的可能
      • 所以作者用了BeeHive来做模块化开发办理东西的命名
    • 我本人很喜欢这个姓名,且现在没想到更好的称号代替ta,咱们不如直接借鉴 这个称号取名:BeeHivePlus
      • Plus在具备原本版别的功用责任之外,有进一步的要求:
      • 添加模块层级办理功用
        • 运用级
        • 事务级
        • 功用级
      • 添加子模块库区别,使其更灵巧应对「大项目」、「小项目」、「SDK开发」、「途径套件开发」
        • 工作办理区别
        • 注册办法区别
    • Capability
      • 这个归于模块办理东西,是移动开发套件的一个模块化开发办理才能,我以为归于整体归于一个Capability
      • 而模块库区别的子库能够作为若干小组件/小插件
        • Plugin: 若是直接选用 第三方开源库BeeHive,咱们能够用Plugin
        • Component: 咱们此次决议借鉴阿里的BeeHive开发一个Swift版别的BeeHivePlus,所以这些应该区别为小功用组件
    • 运用Swift开发
      • Swift至今现已若干年了,ABI早已安稳,所以这次我决议运用Swift开发
      • 相信用Swift开发的BeeHivePlus在代码上,必定会比BeeHive更简练,更高雅
    • 综上,总结一下命名:
      • 完整功用的结构:
        mpe.capability.BeeHivePlus
        • 解释一下: mpe = Mobile Paas Engineering,“移动开发套件PaaS服务工程”
          • mpaas:= Mobile Paas,代表着移动开发套件PaaS服务。咱们自己发明一个结构是能够以为公司搭载MobilePaaS移动开发套件赋能公司和公司事务为愿景的
          • Engineering: 工程
        • capability: 模块办理东西,自身也是一个功用模块
        • BeeHivePlus = 依据BeeHive才能基础上提出进一步的要求
      • 区别的子库以及相关的责任补白:
        • 模块通讯1: mpe.component.urlRouter
          跨运用服务音讯传递、H5页面Web服务(小程序)
        • 模块通讯2: mpe.component.moduleManager
          事务自界说工作
        • 服务办理模块: mpe.capability.serviceManager
          办理服务(注册反注册查询)
        • 音讯办理: mpe.component.modEventMessageManager
          体系级工作+运用级工作
        • 环境与上下文装备: mpe.component.modEnvironment
          AppDelegate+Context+Config
        • 模块分层与模块分层办理分类: mpe.component.modModifier
          • 运用场景: SuperApp、NormalApp、SDK
          • 静态注册: BeeHive.bundle
          • 动态注册: BH_EXPORT_MODULE
          • Module Level分级: Application、Feature、Capability
          • mpe.component.modModifier.superApp
            = 静态注册 + 三级Module Level分级 + 依靠( 模块通讯1 + 模块通讯2 + 服务办理模块 + 音讯办理:
          • mpe.component.modModifier.normalApp
            = 静态注册 + 二级Module Level分级 + 依靠(模块通讯2 + 服务办理模块 + 按需 添加)
          • mpe.component.modModifier.sdk
            = 动态注册 + 一级Module Level分级 + 依靠(按需 添加)
        • 模块分层与模块分层办理分类: mpe.component.modCommon
          TimeProfiler+WatchDog+Common+Defines

总结

本文要点小结

  • 本篇文章以 付出宝 为事例,讲述了:模块的三级分类模块区别战略
  • 紧接着介绍了模块化开发的意义,引出介绍了几款经典的 模块化处理计划,并做了对比剖析
  • 最终,针对剖析中提及的不足,提出了优化计划

本文没有打开的内容

  • 本篇文章没有针对模块化计划规划打开开发实践以及相关demo的介绍和共享
  • 本文也没有进一步讲述运用的分层规划组件化开发实战
  • 一起本文在介绍几款模块化处理计划的过程中,没有进一步针对其间原理进行深化 关于模块化计划开发实践运用的分层规划组件化开发等内容,将在下一篇文章中体现

相关阅览

  • CTMediator 作者的博文:iOS运用架构谈-组件化计划
  • MGJRouter 作者的博文:蘑菇街 App 的组件化之路
  • BeeHive 作者的阿里团队博文:BeeHive:一个高雅但还在完善中的解耦结构

引荐阅览

  • 移动研发途径mPaaS

Swift/OC混编 项意图 模块化实践

  • iOS混编 模块化、组件化、经验指北

运用BeeHive作为模块办理东西

  • 阿里系的手机天猫,运用模块办理东西BeeHive实践 模块化架构
    手机天猫解耦之路
  • 蜂鸟商家版,老项目实践 模块化、组件化 架构演进 事例
    蜂鸟商家版 iOS 组件化 / 模块化实践总结

模块途径化办理东西

这个事例 介绍的 模块化 办理计划,是 搭建途径东西办理模块库 的计划

  • 适用于 事务线广移动端运用多 作为 商业 的大公司
    如京东:京东iOS客户端组件办理实践
  • 适用于 供给PaaSSaaS 服务的 云服务公司
    如:阿里云途径供给的 移动研发途径mPaaS 便是这类

其它

  • 模块化日常:奇特的 pod repo push 失利
  • 模块化日常:CocoaPods 1.4.0 真好用(并不)
  • 模块化日常:CocoaPods 库资源引证问题
  • 模块化日常:重名类
  • 模块化日常:耗时的发布
  • 模块化日常:开源库与私有库重名
  • 模块化日常:库间彼此依靠

专题系列文章

iOS架构规划

  • 1.iOS架构规划|总述
  • 2.iOS架构规划|iOS模块化开发 【模块的三级分类、模块区别战略、模块化计划、模块功用规划】
  • 3.iOS架构规划|依靠包办理东西Cocoapods常用实践:组件库(本地库、长途库(公有、私有))创立、模板工程、区别、资源办理、优化等
  • 4.iOS架构规划|iOS模块化开发实践、iOS组件化开发【组件办理、组件分类、组件区别、组件开发】(待输出)
  • 5.iOS架构规划|iOS开发包二进制化【.a静态库、.framework(静态库、动态库)、.dyld动态库、.xcfameworks等】
  • 6.iOS架构规划|iOS开发-规划办法(待输出)
  • 7.iOS架构规划|架构规划与架构演进(待输出)

探求iOS底层原理

  • 探求iOS底层原理|总述