MVC
Model持有数据,View显示与用户交互的界面,而Controller被定义成最小的可重用单元,负责协调Model与View之间的交互。
但在实际项目中,却因业务的复杂使其变得臃肿,因为它们经常被混杂到View的生命周期中,因此很难说View和ViewController是分离的。
实例
1 | /* Model */ |
不足
传统App中,Model数据只定义了Model的属性,复杂的业务数据处理逻辑只能硬塞给Controller,而Controller需要处理View传来的所有交互,最终导致了控制器成了垃圾箱,越来越不可维护。
为了给Controller瘦身,后来衍生出MVVM和MVP
MVVM
MVVM把Controller当成View,View和Model之间没有紧耦合。ViewModel负责业务处理和数据转化,就是把原来Controller的业务逻辑和页面逻辑等剥离出来放到ViewModel层。
实例
1 | /* Model */ |
优点
低耦合
View 可以独立于Model变化和修改
可重用性
可以把一些视图逻辑放在一个 ViewModel里面,让很多 View 重用这段视图逻辑
可测试
通常界面是比较难于测试的,而 MVVM 模式可以针对 ViewModel 来进行测试
不足
难被调试
数据绑定使得一个位置的 Bug 被快速传递到别的位置,比较难定位
提高维护成本
组件化
随着公司业务的不断发展,项目需求越来越多,各个业务代码耦合也越来越多传统的MVC或者MVVM架构已经无法高效的管理工程代码,急需要一种新技术来更好地管理工程,而组件化是一种能够解决代码耦合的技术。
但是组件化也会使项目体积变大,冷启动速度变慢,运行性能不如传统的架构等的问题。
MGJRouter
采用url-block方案,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中。在使用组件的服务时,通过url找到对应的block,然后获取服务。
使用
1 | // 如果有可变参数(包括 URL Query Parameter)会被自动解析 |
1 | // 常规使用加字典传参 |
不足
组件本身依赖了中间件,且分散注册使的耦合较多
团队合作需要文档说明(URL和参数列表)
网上有说组件多了会造成内存问题是错误的。因为block本身是结构体xxx_block_imp_x(详见“iOS Block分析”),占用20B的内存空间外加外部变量的空间,$2^{20}$个也就占用了20MB而已。
CTMediator
-------------------------------------- | [CTMediator sharedInstance] | | | | openUrl: <<<<<<<<< (AppDelegate) <<<< Call From Other App With URL | | | | | | |/ | | | | parseUrl | | | | | | .................................|............................... | | | | |/ | | | | performTarget:action:params: <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Call From Native Module | | | | | | | | | |/ | | | | ------------- | | | | | | | runtime | | | | | | | ------------- | | . . | ---------------.---------.------------ . . . . . . . . . . . . . . . . -------------------.----------- ----------.--------------------- | . | | . | | . | | . | | . | | . | | . | | . | | | | | | Target | | Target | | | | | | / | \ | | / | \ | | / | \ | | / | \ | | | | | | Action Action Action ... | | Action Action Action ... | | | | | | | | | | | | | |Business A | | Business B | ------------------------------- --------------------------------
这幅图是组件化方案的一个简化版架构描述,主要是基于Mediator模式和Target-Action模式,中间采用了runtime来完成调用。这套组件化方案将远程应用调用和本地应用调用做了拆分,而且是由本地应用调用为远程应用调用提供服务。(引用原作者的图和话)
使用
1 | //以A、B两个子模块为例(作者示例) |
不足
Hard Code,增加Target和Catagory(可忽略)的代码量
performTarget:action:params:shouldCacheTarget会频繁创建和销毁Target对象
当子模块需要调用子模块时,则该子模块就必须依赖CTMediator,这时和MGJRouter是一样的,被子模块依赖。
所以可以改成子模块依赖CTMediator,写Target,通过Target-Action直接调用(直接写其分类不要Target,那么要么依赖别的子模块,要么performSelector自己的方法),这样的话,调用方觉得不爽。
有人会问这样还不如直接通过对象类和方法以及参数来调用了,这样会暴露业务的一些类和方法。
目前本人也没有想到更好的方案,欢迎邮件交流。