小项目框架设计(ReactiveCocoa + MVVM + AFNetworking + FMDB)

上一个项目使用到了 ReactiveCocoa MVVM AFNetworking FMDB框架设计,从最初的尝试,到后来不断思考和学习,现在对这样一个整体设计还是有了一定了理解与心得。在此与大家分享下。

本文将不再过多的描述 ReactiveCocoaMVVMFMDB 的使用细节。关于 ReactiveCocoa,我有一篇实用案例的博客:

http://www.brighttj.com/ios/ios-reactivecocoa-utility-demo.html

文章介绍的更多的是我对这个框架设计的理解,而不是具体代码逻辑的讲解。关于代码逻辑,我会在 Demo 中给出详细的注释,本文 Demo 下载地址:

https://github.com/saitjr/ReactiveCocoa-MVVM-AFNetworking-FMDB.git

环境信息:

Mac OS X 10.11

Xcode 7.0.1

iOS 9.0.1

ReactiveCocoa 2.4.7

AFNetworking 2.6.1

FMDB 2.5

MJExtension 2.5.14


正文

工程目录

先来谈谈工程目录吧,如图:

工程目录
工程目录

1. 【Module】+【Model】

在这个目录中,比较核心的是【Module】与【Model】,他们组成了整个 MVVM 框架。

【Module】与【Model】均包含【Base】,其中有 BaseModelBaseViewModelBaseViewController。在开发中,我还是习惯无论是否需要基类,都去写一个。难免开发之初就考虑到,也难免之后需求会变更。

2. 【Interface】接口

这是借鉴了 Java 中的接口思想,目的是为了统一方法名。例如里面的 SQLInterface.h 文件,就是一个对数据进行 CRUD 操作的 protocol,并且可以规定里面的方法是否必须实现。

3. 【Configuration】配置

对项目的一些基本配置,如基本宏定义、常量、通知名,亦或是 Cell 的 identifier。宏定义中一般包含项目基本属性,如:主色调、常用方法等。

在提供的 Demo 中,我将 SQL 语句放在了 SQL.h 中,是因为 SQL 只有一个文件在引用,其中的定义方式是:

static NSString * const selectArticleSQL = @"SELECT * FROM article";

而 NotificationNames.h 会在大部分文件中用到,所以使用 UIKIT_EXTERN 定义为了全局变量:

-----.h
UIKIT_EXTERN NSString * const LoadAllNotification;
-----.m
NSString * const LoadAllNotification = @"LoadAllNotification";

5. 【Category】类目

项目中没有打包的类目,例如给三方或系统类新增的一些方法。

RAC+MVVM

RAC+MVVM在【Module】和【Model】这两个目录中进行实现。在这之中,MVVM 是框架思想,RAC 只是辅助而已。

一、MVVM

之所以采用 MVVM,而不是 MVC,也得益于 MVVM 的一大特点,就是减轻 C 层的负担,毕竟以前的 C 层完全就是百宝箱,什么样的代码都写在里面。

对于 MVVM 我的理解和拆分是这样的:

1. Model与View

这一层和 MVC 中的 Model、View 含义相同。

2. ViewModel

这一层主要作用是将以前写在 ViewController 中的数据处理放在 ViewModel 中,如:网络请求、数据缓存、无法直接展示的数据处理(如 NSNumber 这类的,就在 VM 中处理成 NSString,然后 V 层直接用,而不是在 V 层中处理)。

二、Demo中VC的设计

图形结合源码应该能看个大概。

VC设计
VC设计

三、自定义 cell 的设计(可延伸至自定义 View 的设计)

因为介绍的是 VC,所以这里再单独说一下 HomePageCell 这个自定义 cell 的设计。

因为想到实际项目中,可能会有比较复杂的 cell,所以 Demo 中写的是一个比较完整的设计方式(如果单单看这个 Demo 的话,这个自定义 cell 太简单,没必要有一个单独的 VM,有点过度设计)。

cell 中的构思是,cell 有一个 CellVM 来管理 cell 中要显示的数据,CellVM 来自于 VC 中,dataSource 数组。处理方式具体是:在网络请求完成以后,将字典->模型,然后通过模型,初始化 CellVM,然后将 CellVM 放入 dataSource 数组。

Cell 实现部分代码如下:

@implementation HomePageCell

- (void)awakeFromNib {
    [super awakeFromNib];
    
    [self setupSignal];
}

- (void)setupSignal {
    
    @weakify(self);
    [RACObserve(self, viewModel) subscribeNext:^(HomePageCellViewModel *viewModel) {
        
        @strongify(self);
        self.textLabel.text = viewModel.titleText;
        self.detailTextLabel.text = viewModel.authorText;
    }];
}

@end

AFNetworking

前几天,AFNetworking 升级到了 3.0。将以前基于 NSURLConnection 的 API,全都改成了 NSURLSession,关于更新的详情,可以看AFNetworking 3.0 迁移指南这篇文章。

在页面销毁、重新请求等情况下,需要将还在队列中的请求取消,以免占用资源。

考虑到这一点,结合我的网络请求在 VM 发送,权衡再三,在 BaseViewModel 的基础上,再进行了一次封装—— RequestViewModel。这个 VM 有 AFHTTPSessionManager 类的属性,一个该属性的懒加载和一个在 dealloc 中取消请求方法。

在以前使用 MVC 的时候,我会对 AFNetworking 进行再次的封装,这样更像是一个 MVCS 的设计,目的是防止 VC 过重,现在把这部分代码扔在了 VM 中,看起来还好,所以并没有对 AFNetworking 再次封装。关于以前的设计方式,可以看这篇文章:

网络请求框架封装

FMDB

FMDB 提供了一种线程安全的模式,在这之中维护这一个串行队列。

1. 初始化

初始化方式参考了开源项目 MVVMReactiveCocoa,作者采用了类目的形式给了一个单例:

@implementation FMDatabaseQueue (Extension)

+ (instancetype)shareInstense {
    
    static FMDatabaseQueue *queue = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = [FMDatabaseQueue databaseQueueWithPath:DB_PATH];
    });
    
    return queue;
}

@end

2. 统一接口

关于 CRUD 方法的定义,借鉴了 Java Interface 的设计:

3. SQL语句

根据实际情况,SQL 语句参数可以采用?的形式,也可以采用 :keyword 的形式。

INSERT INTO myTable VALUES (?, ?, ?)
INSERT INTO myTable VALUES (:id, :name, :value)

关于数据库的创建与更新,我并不建议在 Bundle 中包含 xxx.sql 这样的文件,因为他们会在编译后一起打包,用户下载解压就能看到,并不怎么安全。目前我是直接在程序中写的 SQL,不知道大家有没有更好的方式。

4. CRUD

在网络请求成功的时候,存入数据。存储是一个批量的操作,建议采用事务 inTransaction 实现。简单的操作采用 inDatabase 即可。

FMDatabaseQueue 是一个串行队列,并且 inTransaction 和 inDatabase 都是同步线程,需要注意的是不要在 block 中执行另一个数据库访问操作,防止线程死锁

最后

每个项目完后,都会有很多收获,有很多东西需要整理总结。写这篇博客的原因有两个:

原因之一:因为我在开发过程中踩了不少坑,可能开发到中途,发现框架设计不好。框架如何设计,并没有一个标准答案,而且设计思想,还需要在不断实践中得出,所以每次总结,是为了给自己看,也是为了帮到其他有同样困扰的朋友。

原因之二:也正是因为我不知道框架到底怎么样,所以写出来,让大家看看,都多多提出建议。谢谢。

参考

1. 雷纯峰的开源项目 MVVMReactiveCocoa,这个项目给我的启发很大,在此谢谢作者,也为开源点赞

2. iOS 大型项目开发漫谈

3. iOS 应用架构谈 网络设计方面

《小项目框架设计(ReactiveCocoa + MVVM + AFNetworking + FMDB)》有6个想法

  1. 请问我下载了MVVMReactiveCocoa使用Cocoapod更新完以后没有没找找到WXTabBarController。因此一直不能运行。

发表评论

电子邮件地址不会被公开。