前言
从事 ios 开发 8 年多了,结合自己的经验记录我个人认为灵活的 ios 架构,顺便学习Swift(我们工程还是以OC为主)。由于我个人水平有限所写之处,必然有所疏漏,望斧正。 非常希望,大家提出自己的建议,或场景需求。我也将持续更新
这里,我将架构分成两块:1. 工程结构( 目录结构) 2.架构。 因为,我们在开发之初就要确定目录结构,我发现这也非常重要。 我的讲解方式是 抛出问题,给出解决方案,最后给出代码,分析我这样写的原因(这是我认为最重要的
)。
工程代码GitHub
我用了子模块请使用 git clone --recursive https://github.com/daviad/Architect.git
下载
我使用了Pod 请pod install
问题
-
工程结构( 目录结构)
对一个工程我们除了有源文件,还有其他辅助文件帮助我们管理文件。
源文件的获取,权限,管理, 工程的配置。 -
模块增删与通讯
随这项目的发展,模块越来越多,有些文件变得越来越大,比如:AppDelegate。有些模块删除了,但是与之相关的代码却无法删除干净
每个模块都有个说明文档:module.md
- 数据库模块
- 网络模块
- weex 模块
- 登陆模块
- 日志模块
- 业务模块
- 。。。。
- mvvm的架构
- test
- 持续集成
解决思路和方案
工程结构
这里我用 workspace 结合cocopods、xcconfig配置文件、git git子模块、 脚本解决工程目录问题
工程目录 Architect目录下的内容.jpg- archived:打包后的文件
- doc:帮助说明文件
- configuration:工程配置文件
- script:脚本
- thirdParty:没有被Pod管理的第三方库。一般是 xxx.a\xxx.framework
- tools:一些工具比如:
代码放置位置
源码 | 方式 | 优点 |
---|---|---|
第三方库 | Podfile | 简单方便 |
第三方库不满足需求 | 私有pod | 便于控制 |
自己封装的库 | 私有pod | 便于管理 |
自己代码模块 | git 子模块 | 便于控制,根据情况引入子模块 ,效率高 |
详细说明:
- 对于 比较稳定的 第三方库,我们可以直接用podfile 引入,
- 第三方库有bug或不能满足我们自己项目的某些需求,二我们又必须修改第三方源码,就在本地(本地服务器)对第三方库修改,用pod引入我们自己的库。
- 对于我们自己的代码,如果是比较稳定的我们可以做Pod私有库。
- 对于我们自己的某些模块化的,但并不足于做成库的(并且库太多影响程序启动的效率),我们可以用 git 子模块 引入。一般我们的 业务模块可以用这种方式。比如我们有a、b、c 三个模块,开发小组要开发一个新模块(业务功能),这个功能和b、c都无关、在发开发时,就可以只引入a模块。这样编译的效率就更高。
关于pod的私有库使用,可以参考
这里多说两句,Podfile 可以指定Workspace,和Target 的目录。
git 要知道使用ignore文件。因为我发现好几个工作好多年的人不知道ignore是何物。
配置文件使用
直接使用xcode配置工程虽然简单,但是不够灵活,有时比较麻烦,比如,你想从新创建一个类似功能的工程,需要把相同的 库再加一遍谁做谁痛苦,用xccofing文件就能很好解决这个问题。所以我用xcconfig结合xcode一起配置工程。
具体的代码工程实现
xcode中使用配置文件
- 新建后缀xcconfig文件(参考Pod的配置文件)这里我的是 Architect.debug.xcconfig 应该区分debug 和 release 的这里我简单点就用了同一个文件。
- 根据需要填写配置文件
#include "./Pods/Target Support Files/Pods-Architect/Pods-Architect.debug.xcconfig"
-
看图操作
配置文件使用.jpg
新建模块的脚本
创建Test模块。 cd 到 scripts 目录下 运行
python gen_module.py Test
git子模块的操作
详细操作网上有很多例子,这里列出我使用的命令
git submodule add https://github.com/daviad/Chat.git src/Architect/GitSubmodule/Chat
模块管理
模块管理主要包括:加载、设置、模块间通信。 模块的删除在工程结构部分已经解决,基础固定模块一般做Pod库。
加载、设置
模块通过 ModuleProtocl ModuleManager 进行管理。主要代码
protocol ModuleProtocl {
// 为了避免模块顺序引起的bug。才有了load 和setup
//加载模块,实质就是创建对象
func load()
//设置模块,此时所有的模块都已经load完成
func setup()
//数据库的相关操作。 需要数据库的模块才需要这个这个属性,按理说应该再建一个协议(DBModuleProtocl),但是那样写的话使用好麻烦,目前我没有找到一个更好的方式。就是说如何完成数组中的元素可以分别实现不同的协议。(Any ,继承协议,都需要强转,感觉泛型好像可以,学艺不精,没写出来)
var dbModels: [DBModel]? { get }
}
extension ModuleProtocl {
func setup() {}
func load() {}
var dbModels: [DBModel]? { return nil }
}
final class ModuleManager {
static let shared = ModuleManager()
private init() {}
// TODO: 此处可以通过反射 写在配置文件生成 或不用反射 结合脚本生成代码 提高效率
// let modules: [ModuleProtocl] = [MainPageModule(),UserModule()]
// 是否将 module 分类 比如 必须先加载的?
private(set) var modules: [ModuleProtocl] = [ModuleProtocl]()
func loadModules() {
let mainPage = MainPageModule()
mainPage.load()
modules.append(mainPage)
let userModule = UserModule()
userModule.load()
modules.append(userModule)
let dataBaseModule = DataBaseModule()
dataBaseModule.load()
modules.append(dataBaseModule)
_ = modules.map{ $0.setup() }
}
}
代码不多解释一看就能懂,主要说说这么做的好处和我的想法:
- 定义协议,代码结构清晰。
- 协议的 extension 可以实现默认实现
- 可以统一管理module,以后不管module怎么变,我的框架这部分都不需要改代码。并且删除模块代码非常干净。
- load() setup()主要是时机不同,处理了模块加载顺序依赖问题。
有的模块需要响应 appdelegate 的事件,为了模块分离,所以定义了这个协议方法,
关于模块的注册,可以用一个plist 文件,可可以直接写,可以用脚本生成代码
代码分离性更高 代码易懂效率高 要写脚本使用不连贯 但在这里我认为影响都不大因为模块不会很多。
目前不够完美的地方:
var dbModels: [DBModel]? { get }
dbModels 是 数据库的相关操作。 需要数据库的模块才需要这个这个属性,按理说应该再建一个协议(DBModuleProtocl),但是那样写的话使用好麻烦,目前我没有找到一个更好的方式。就是说如何完成数组中的元素可以分别实现不同的协议。(Any ,继承协议,都需要强转,感觉泛型好像可以,学艺不精,没写出来)
模块内部结构以及实现
我这里使用MVVM的模式开发。目录结构如下: 见我前面的脚本
各个模块实现
数据库模块
如果项目需要数据库,这将是一个很基础的模块 具体参考 fmdb swift版本的封装
网络模块
持续完成中。。。。
网友评论