• 【iOS】小项目框架设计(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是一个串行队列,并且inTransactioninDatabase都是同步线程,需要注意的是不要在block中执行另一个数据库访问操作,防止线程死锁

    最后

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

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

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

    参考

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

    2. iOS大型项目开发漫谈

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

  • 相关阅读:
    MSF——客户端渗透之VBScript感染
    (136)leetcode刷题Python笔记——只出现一次的数字
    两道CTF Web的题目
    (1)leetcode刷题Python笔记——两数之和
    Pikachu漏洞练习平台实验——越权漏洞(八)
    Pikachu漏洞练习平台实验——php反序列化、XXE、SSRF(九)
    MVP Docs Champion (2020)
    Azure DevOps Server 集成操作Windows系统文件管理器
    Azure DevOps Server (TFS) 数据分析Analytics(一):使用OData扩展分析服务数据
    Azure DevOps Server (TFS)搜索功能三:wiki维基搜索
  • 原文地址:https://www.cnblogs.com/yujidewu/p/5741152.html
Copyright © 2020-2023  润新知