【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應用架構談 網絡設計方面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM