使用FMDB做離線緩存的例子(iOS)

本文以仿微博的應用為基礎倡蝙,實現(xiàn)使用FMDB做離線緩存

仿微博下拉

仿微博上拉

設計思路:

分析加載微博過程:
  1. 嘗試從沙盒加載緩存數(shù)據(jù)
  2. 有緩存,直接加載緩存
  3. 無緩存踢步,發(fā)送請求癣亚,展示返回的數(shù)據(jù),將數(shù)據(jù)存入沙盒


    加載微博過程.png
分析微博返回數(shù)據(jù):
  1. 需要加載的微博多获印,數(shù)據(jù)量大述雾,不適合用plist和NSCoding這類一次性加載和存儲全部數(shù)據(jù)的方法,使用數(shù)據(jù)庫則可以做到取一部分數(shù)據(jù)和存一部分的數(shù)據(jù)
  2. 由于微博模型多兼丰,如果按照后臺服務器一樣玻孟,一個模型建一張表,每個表(如:用戶鳍征,微博取募,圖片,文字)又相互之間通過外鍵聯(lián)系蟆技,會導致客戶端的數(shù)據(jù)庫過于復雜玩敏,所以只建一張表
  3. 每條微博的字段非常多,在客戶端數(shù)據(jù)庫表中也創(chuàng)建相應數(shù)量的字段保存數(shù)據(jù)是不適合的质礼,所以可以在數(shù)據(jù)庫表中只創(chuàng)建一個blob類型的status字段保存每條微博的全部數(shù)據(jù)
  4. 每條微博是根據(jù)自身字段idstr大小來比較新舊的旺聚,把每條微博從數(shù)據(jù)庫取出再取出idstr來比較新舊是不適合的,所以表中還應該增加idstr字段方便查詢
  5. 最終確定建一張表眶蕉,三個字段:id(系統(tǒng)默認)砰粹,status,idstr
微博模型較多

微博的字段非常多

表結構

實現(xiàn)步驟

步驟1.創(chuàng)建一個工具類StatusTool

步驟2.在StatusTool的initialize中初始化數(shù)據(jù)庫

static FMDatabase *_db;
+ (void)initialize
{
    // 1.打開數(shù)據(jù)庫
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"statuses.sqlite"];
    _db = [FMDatabase databaseWithPath:path];
    [_db open];
    
    // 2.創(chuàng)表
    [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_status (id integer PRIMARY KEY, status blob NOT NULL, idstr text NOT NULL);"];
}```

步驟3. StatusTool實現(xiàn)存儲方法
  • (void)saveStatuses:(NSArray *)statuses
    {
    // 要將一個對象存進數(shù)據(jù)庫的blob字段,最好先轉為NSData
    // 一個對象要遵守NSCoding協(xié)議,實現(xiàn)協(xié)議中相應的方法,才能轉成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實現(xiàn)取緩存方法

+ (NSArray *)statusesWithParams:(NSDictionary *)params
{
    // 根據(jù)請求參數(shù)生成對應的查詢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"]];
    }
    
    // 執(zhí)行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.方法調用:

/**

  • 下拉刷新造挽,加載最新的數(shù)據(jù)
    */
  • (void)loadNewStatus
    {
    // 1.拼接請求參數(shù)
    碱璃。。饭入。嵌器。。谐丢。爽航。
    params[@"since_id"] = firstStatusF.status.idstr;

    // 2.先嘗試從數(shù)據(jù)庫中加載微博數(shù)據(jù)
    NSArray statuses = [StatusTool statusesWithParams:params];
    if (statuses.count) { // 數(shù)據(jù)庫有緩存數(shù)據(jù)
    /
    ……….處理數(shù)據(jù), 展示返回的數(shù)據(jù)*/
    } else { // 數(shù)據(jù)庫沒緩存數(shù)據(jù)
    // 發(fā)送請求
    [HttpTool get:@"https://api.weibo.com/2/statuses/friends_timeline.json" params:params success:^(id json) {
    // 緩存新浪返回的字典數(shù)組
    [StatusTool saveStatuses:json[@"statuses"]];

          /* ……….處理數(shù)據(jù) , 展示返回的數(shù)據(jù)*/
      } failure:^(NSError *error) {
          蚓让。。讥珍。历极。
      }];
    

    }
    }

/**

  • 上拉刷新,加載更多的微博數(shù)據(jù)
    */
  • (void)loadMoreStatus
    {
    // 1.拼接請求參數(shù)
    衷佃。趟卸。。氏义。锄列。。觅赊。
    params[@"max_id"] = @(maxId);

    // 2. 先嘗試從數(shù)據(jù)庫中加載微博數(shù)據(jù)
    NSArray statuses = [StatusTool statusesWithParams:params];
    if (statuses.count) { // 數(shù)據(jù)庫有緩存數(shù)據(jù)
    /
    ……….處理數(shù)據(jù), 展示返回的數(shù)據(jù)*/
    } else { // 數(shù)據(jù)庫沒緩存數(shù)據(jù)
    // 發(fā)送請求
    [HttpTool get:@"https://api.weibo.com/2/statuses/friends_timeline.json" params:params success:^(id json) {
    // 緩存新浪返回的字典數(shù)組
    [StatusTool saveStatuses:json[@"statuses"]];

          /* ……….處理數(shù)據(jù), 展示返回的數(shù)據(jù)*/
      } failure:^(NSError *error) {
          。琼稻。吮螺。。帕翻。鸠补。
      }];
    

    }
    }```

遇到問題:

如果把NSDictionary字典數(shù)據(jù)status直接通過[_db executeUpdateWithFormat:@"INSERT INTO t_status(status, idstr) VALUES (%@, %@);", status, status[@"idstr"]];來存儲,就會取不出來嘀掸。
原因是通過%@來傳入字典對象紫岩,相當于傳入[status description];,打印類型可以發(fā)現(xiàn)取出的是字符串睬塌,不是我們預期的字典對象泉蝌。
解決方法:
把字典用NSData *statusData = [NSKeyedArchiver archivedDataWithRootObject:status];轉成NSData再存入數(shù)據(jù)庫。
取出數(shù)據(jù)時揩晴,用NSDictionary *status = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];再轉成字典勋陪。

為什么字典對象能轉成NSData類型

字典對象能轉成NSData類型的本質是遵守了NSCoding協(xié)議,所以如果想要把自定義對象轉成NSData類型需要遵守NSCoding協(xié)議硫兰,實現(xiàn)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```
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末诅愚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子劫映,更是在濱河造成了極大的恐慌违孝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泳赋,死亡現(xiàn)場離奇詭異雌桑,居然都是意外死亡,警方通過查閱死者的電腦和手機祖今,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門筹燕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事撒踪」В” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵制妄,是天一觀的道長掸绞。 經常有香客問我,道長耕捞,這世上最難降的妖魔是什么衔掸? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮俺抽,結果婚禮上敞映,老公的妹妹穿的比我還像新娘。我一直安慰自己磷斧,他們只是感情好振愿,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弛饭,像睡著了一般冕末。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侣颂,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天档桃,我揣著相機與錄音,去河邊找鬼憔晒。 笑死藻肄,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拒担。 我是一名探鬼主播仅炊,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼澎蛛!你這毒婦竟也來了抚垄?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谋逻,失蹤者是張志新(化名)和其女友劉穎呆馁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毁兆,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡浙滤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了气堕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纺腊。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡畔咧,死狀恐怖,靈堂內的尸體忽然破棺而出揖膜,到底是詐尸還是另有隱情誓沸,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布壹粟,位于F島的核電站拜隧,受9級特大地震影響,放射性物質發(fā)生泄漏趁仙。R本人自食惡果不足惜洪添,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雀费。 院中可真熱鬧干奢,春花似錦、人聲如沸盏袄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽貌矿。三九已至炭菌,卻和暖如春罪佳,著一層夾襖步出監(jiān)牢的瞬間逛漫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工赘艳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留酌毡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓蕾管,卻偏偏與公主長得像枷踏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子掰曾,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評論 25 707
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫旭蠕、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,105評論 4 62
  • 【YTT燭光公益圖書館】 學習易效能到現(xiàn)在旷坦,已經堅持踐行第48天了掏熬,這是我人生的第一個月度檢視,也是我人生第...
    大道之簡三石閱讀 438評論 2 2
  • 記得離開香港那時秒梅,也是抑郁心痛的不得了旗芬。剛剛看香港的影片,又講到嶺南捆蜀,看到影片中那些似曾相識的同學疮丛,建筑幔嫂。就想起剛...
    言姱閱讀 261評論 0 0
  • 1.js中有哪些數(shù)據(jù)類型,并解釋清楚原始數(shù)據(jù)類型和引用數(shù)據(jù)類型 js中共有null , undefined , s...
    約翰碼農閱讀 442評論 0 3