使用FMDB做离线缓存的例子(iOS)

本文以仿微博的应用为基础,实现使用FMDB做离线缓存

仿微博下拉

仿微博上拉

设计思路:

分析加载微博过程:
  1. 尝试从沙盒加载缓存数据
  2. 有缓存,直接加载缓存
  3. 无缓存,发送请求,展示返回的数据,将数据存入沙盒


    加载微博过程.png
分析微博返回数据:
  1. 需要加载的微博多,数据量大,不适合用plist和NSCoding这类一次性加载和存储全部数据的方法,使用数据库则可以做到取一部分数据和存一部分的数据
  2. 由于微博模型多,如果按照后台服务器一样,一个模型建一张表,每个表(如:用户,微博,图片,文字)又相互之间通过外键联系,会导致客户端的数据库过于复杂,所以只建一张表
  3. 每条微博的字段非常多,在客户端数据库表中也创建相应数量的字段保存数据是不适合的,所以可以在数据库表中只创建一个blob类型的status字段保存每条微博的全部数据
  4. 每条微博是根据自身字段idstr大小来比较新旧的,把每条微博从数据库取出再取出idstr来比较新旧是不适合的,所以表中还应该增加idstr字段方便查询
  5. 最终确定建一张表,三个字段:id(系统默认),status,idstr
微博模型较多

微博的字段非常多

表结构

实现步骤

步骤1.创建一个工具类StatusTool

步骤2.在StatusTool的initialize中初始化数据库

static FMDatabase *_db;
+ (void)initialize
{
    // 1.打开数据库
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"statuses.sqlite"];
    _db = [FMDatabase databaseWithPath:path];
    [_db open];
    
    // 2.创表
    [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_status (id integer PRIMARY KEY, status blob NOT NULL, idstr text NOT NULL);"];
}```

步骤3. StatusTool实现存储方法
  • (void)saveStatuses:(NSArray *)statuses
    {
    // 要将一个对象存进数据库的blob字段,最好先转为NSData
    // 一个对象要遵守NSCoding协议,实现协议中相应的方法,才能转成NSData
    for (NSDictionary *status in statuses) {
    // NSDictionary --> NSData
    NSData *statusData = [NSKeyedArchiver archivedDataWithRootObject:status];
    [_db executeUpdateWithFormat:@"INSERT INTO t_status(status, idstr) VALUES (%@, %@);", statusData, status[@"idstr"]];
    }
    }```

步骤4. StatusTool实现取缓存方法

+ (NSArray *)statusesWithParams:(NSDictionary *)params
{
    // 根据请求参数生成对应的查询SQL语句
    NSString *sql = nil;
    if (params[@"since_id"]) { // 下拉刷新
        sql = [NSString stringWithFormat:@"SELECT * FROM t_status WHERE idstr > %@ ORDER BY idstr DESC LIMIT 20;", params[@"since_id"]];
    } else if (params[@"max_id"]) { // 上拉刷新
        sql = [NSString stringWithFormat:@"SELECT * FROM t_status WHERE idstr <= %@ ORDER BY idstr DESC LIMIT 20;", params[@"max_id"]];
    }
    
    // 执行SQL
    FMResultSet *set = [_db executeQuery:sql];
    NSMutableArray *statuses = [NSMutableArray array];
    while (set.next) {
        NSData *statusData = [set objectForColumnName:@"status"];
        NSDictionary *status = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];
        [statuses addObject:status];
    }
    return statuses;
}```

步骤5.方法调用:

/**

  • 下拉刷新,加载最新的数据
    */
  • (void)loadNewStatus
    {
    // 1.拼接请求参数
    。。。。。。。
    params[@"since_id"] = firstStatusF.status.idstr;

    // 2.先尝试从数据库中加载微博数据
    NSArray statuses = [StatusTool statusesWithParams:params];
    if (statuses.count) { // 数据库有缓存数据
    /
    ……….处理数据, 展示返回的数据*/
    } else { // 数据库没缓存数据
    // 发送请求
    [HttpTool get:@"https://api.weibo.com/2/statuses/friends_timeline.json" params:params success:^(id json) {
    // 缓存新浪返回的字典数组
    [StatusTool saveStatuses:json[@"statuses"]];

          /* ……….处理数据 , 展示返回的数据*/
      } failure:^(NSError *error) {
          。。。。
      }];
    

    }
    }

/**

  • 上拉刷新,加载更多的微博数据
    */
  • (void)loadMoreStatus
    {
    // 1.拼接请求参数
    。。。。。。。
    params[@"max_id"] = @(maxId);

    // 2. 先尝试从数据库中加载微博数据
    NSArray statuses = [StatusTool statusesWithParams:params];
    if (statuses.count) { // 数据库有缓存数据
    /
    ……….处理数据, 展示返回的数据*/
    } else { // 数据库没缓存数据
    // 发送请求
    [HttpTool get:@"https://api.weibo.com/2/statuses/friends_timeline.json" params:params success:^(id json) {
    // 缓存新浪返回的字典数组
    [StatusTool saveStatuses:json[@"statuses"]];

          /* ……….处理数据, 展示返回的数据*/
      } failure:^(NSError *error) {
          。。。。。。
      }];
    

    }
    }```

遇到问题:

如果把NSDictionary字典数据status直接通过[_db executeUpdateWithFormat:@"INSERT INTO t_status(status, idstr) VALUES (%@, %@);", status, status[@"idstr"]];来存储,就会取不出来。
原因是通过%@来传入字典对象,相当于传入[status description];,打印类型可以发现取出的是字符串,不是我们预期的字典对象。
解决方法:
把字典用NSData *statusData = [NSKeyedArchiver archivedDataWithRootObject:status];转成NSData再存入数据库。
取出数据时,用NSDictionary *status = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];再转成字典。

为什么字典对象能转成NSData类型

字典对象能转成NSData类型的本质是遵守了NSCoding协议,所以如果想要把自定义对象转成NSData类型需要遵守NSCoding协议,实现encodeWithCoder和initWithCoder方法

例:自定义对象

@interface Shop : NSObject <NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) double  price;
@end

@implementation Shop
- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeDouble:self.price forKey:@"price"];
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        self.name = [decoder decodeObjectForKey:@"name"];
        self.price = [decoder decodeDoubleForKey:@"price"];
    }
    return self;
}
@end```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,389评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 【YTT烛光公益图书馆】 学习易效能到现在,已经坚持践行第48天了,这是我人生的第一个月度检视,也是我人生第...
    大道之简三石阅读 431评论 2 2
  • 记得离开香港那时,也是抑郁心痛的不得了。刚刚看香港的影片,又讲到岭南,看到影片中那些似曾相识的同学,建筑。就想起刚...
    言姱阅读 255评论 0 0
  • 1.js中有哪些数据类型,并解释清楚原始数据类型和引用数据类型 js中共有null , undefined , s...
    约翰码农阅读 431评论 0 3