本地化存储也是app开发当中比较常见的功能需求。比如一些列表界面(tableview)相关的数据存储。
本文就以tableview界面数据的存储为例。
为简单起见,demo中使用了MJExtension及MJRefresh框架,采用常用的MVC代码结构,我们将在此基础上扩展其本地化存储功能。列表界面的核心代码如下(灰色背景的部分表示将要实现和扩展的方法):
- (void)setupRefresh {
self.tableView.mj_header = [XMGRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewTopics)];
[self.tableView.mj_header beginRefreshing];
self.tableView.mj_footer = [XMGRefreshFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreTopics)];
}
#pragma mark - 数据加载
- (void)loadNewTopics {
// 取消所有请求
[self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
// 参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"a"] = self.aParam;
params[@"type"] = @(self.type);
__weak typeof(self) weakSelf = self;
// 发送请求
[self.manager GET:XMGCommonURL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
// 存储maxtime(方便用来加载下一页数据)
weakSelf.maxtime = responseObject[@"info"][@"maxtime"];
// 字典数组 -> 模型数组
weakSelf.topics = [XMGTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
// 2. 在本地sql做缓存 todo
[weakSelf saveToSql: weakSelf.topics ] ;
// 刷新表格
[weakSelf.tableView reloadData];
// 让[刷新控件]结束刷新
[weakSelf.tableView.mj_header endRefreshing];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 让[刷新控件]结束刷新
[weakSelf.tableView.mj_header endRefreshing];
// 加载缓存、本地数据库 todo
[weakSelf getFromeSql];
// 刷新表格
[weakSelf.tableView reloadData];
}];
}
- (void)loadMoreTopics
{
// 取消所有的请求
[self.manager.tasks makeObjectsPerformSelector:@selector(cancel)];
// 参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"a"] = self.aParam;
params[@"maxtime"] = self.maxtime;
params[@"type"] = @(self.type);
__weak typeof(self) weakSelf = self;
// 发送请求
[self.manager GET:XMGCommonURL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
// 存储这页对应的maxtime
weakSelf.maxtime = responseObject[@"info"][@"maxtime"];
// 字典数组 -> 模型数组
NSArray<XMGTopic *> *moreTopics = [XMGTopic mj_objectArrayWithKeyValuesArray:responseObject[@"list"]];
[weakSelf.topics addObjectsFromArray:moreTopics];
// 插入新增的数据至数据库
[[SQLiteManager shareSQLiteManager] saveTopics:moreTopics];
// 刷新表格
[weakSelf.tableView reloadData];
// 让[刷新控件]结束刷新
[weakSelf.tableView.mj_footer endRefreshing];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
XMGLog(@"请求失败 - %@", error);
// 让[刷新控件]结束刷新
[weakSelf.tableView.mj_footer endRefreshing];
}];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.topics.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
XMGTopicCell *cell = [tableView dequeueReusableCellWithIdentifier:XMGTopicCellId];
cell.topic = self.topics[indexPath.row];
return cell;
}
#pragma mark - 代理方法
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// return self.topics[indexPath.row].cellHeight;
return 600;
}
这是面向模型的开发模式,模型类相关属性如下:
@interface XMGTopic : NSObject
/** 用户的名字 */
@property (nonatomic, copy) NSString *name;
/** 用户的头像 */
@property (nonatomic, copy) NSString *profile_image;
/** 帖子的文字内容 */
@property (nonatomic, copy) NSString *text;
/***** 额外增加的属性 - 方便开发 *****/
/** cell的高度 */
@property (nonatomic, assign) CGFloat cellHeight;
/** 中间内容的frame */
@property (nonatomic, assign) CGRect contentF;
@end
其实如果单纯的实现存储的功能,简单粗暴的方法就是直接将模型、或者原始的字典或数组整体进行存储。但是这样的话就不方便相关的统计、搜索,后期需要扩展这些功能的话笨重而不灵活。
所以这里就将模型属性一一对应进行sql的数据存储。设计一个工具类SQLiteManager,核心代码如下(本次旨在实现功能,代码还可以优化重构,或采用运行时机制减少侵入性):
#import "SQLiteManager.h"
#import <sqlite3.h>
#import "XMGTopic.h"
@interface SQLiteManager ()
{ sqlite3 *db;}
@end
@implementation SQLiteManager
+ (instancetype)shareSQLiteManager {
static SQLiteManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[SQLiteManager alloc] init];
});
return instance;
}
- (instancetype)init
{
if (self = [super init]) {
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [path stringByAppendingPathComponent:@"demo.sqlite"];
NSLog(@"%@", filePath);
if (sqlite3_open([filePath UTF8String], &db) == SQLITE_OK)
{ NSLog(@"打开成功");
[self createTable];
}
}
return self;
}
- (BOOL)createTable {
NSString *sql = @"CREATE TABLE IF NOT EXISTS t_topic(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, profile_image TEXT, text TEXT, cellHeight FLOAT);";
return [self execSQL:sql];
}
- (BOOL)dropTable {
NSString *sql = @"drop table if exists t_stu;";
return [self execSQL:sql];
}
- (BOOL)execSQL:(NSString *)sql {
return sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL) == SQLITE_OK;
}
- (void)saveTopics:(NSArray *)topics {
for(XMGTopic* topic in topics) {
NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_topic(name, profile_image, text, cellHeight) VALUES(‘%@‘, ‘%@‘, ‘%@‘, %f);", topic.name, topic.profile_image, topic.text, topic.cellHeight];
sqlite3_stmt *stmt = nil;
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil) != SQLITE_OK) return;
if (sqlite3_step(stmt) == SQLITE_DONE){
NSLog(@"插入一条记录成功!");
}
sqlite3_finalize(stmt);
}
}
// 返回模型数组
- (NSArray *)getTopics {
NSString *sql = @"select * from t_topic;";
// 准备语句
sqlite3_stmt *stmt = nil;
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, nil) != SQLITE_OK) {
NSLog(@"准备语句创建失败!");
return nil;
}
NSMutableArray *arrM = [NSMutableArray array];
while (sqlite3_step(stmt) == SQLITE_ROW) {
int count = sqlite3_column_count(stmt);
id value;
for (int i = 0; i < count; ++i) {
const char *cName = sqlite3_column_name(stmt, i);
NSString *columnNameStr = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
NSLog(@"ddddddd%@",columnNameStr);
int type = sqlite3_column_type(stmt, i);
switch (type) {
case SQLITE_INTEGER:
{ int value = sqlite3_column_int(stmt, i);
}
break;
case SQLITE_FLOAT:
{ double value = sqlite3_column_double(stmt, i);
}
break;
case SQLITE3_TEXT:
{
const char *textValue = sqlite3_column_text(stmt, i);
value = [NSString stringWithCString:textValue encoding:NSUTF8StringEncoding];
}
break;
case SQLITE_NULL:
break;
default:
break;
}
XMGTopic *topicItem = [[XMGTopic alloc]init];
if ([columnNameStr isEqualToString: @"name"]) {
topicItem.name = (NSString *)value;
}
if ([columnNameStr isEqualToString: @"profile_image"]) {
topicItem.profile_image = (NSString *)value;
}
if ([columnNameStr isEqualToString: @"text"]) {
topicItem.text = (NSString *)value;
}
if ([columnNameStr isEqualToString: @"cellHeight"]) {
topicItem.cellHeight = [value doubleValue];
}
[arrM addObject:topicItem];
}
}
return [arrM copy];
}
@end
现在就可以实现存储的这两个方法了。
- (NSArray *)getFromeSql {
if (topics != nil) return ;
return [[SQLiteManager shareSQLiteManager] getTopics];
}
- (void)saveToSql:(NSArray *)topics {
[[SQLiteManager shareSQLiteManager] dropTable];
//删除表后需要重新创建表单,此时单例是没有表的。注意理解单例
[[SQLiteManager alloc] init] ;
[[SQLiteManager shareSQLiteManager] saveTopics:weakSelf.topics];
}
原文:http://www.cnblogs.com/imsz/p/6262857.html