iOS離線緩存

MarMebane 8年前發布 | 13K 次閱讀 數據庫 iOS開發 Objective-C開發

來自: http://www.youngmeng.com/2016/02/12/iOS離線緩存/

為了節省流量和更好的用戶體驗,目前很多應用都使用本地緩存機制,不需要每次打開app的時候都加載數據,或者重新向服務器請求數據,因此可以把每次瀏覽的數據保存到沙盒中,當下次打開軟件的時候,首先從沙盒加載緩存的數據,或者當app未聯網的時候,從沙盒中加載之前緩存的舊數據。

離線數據的方法選擇

  1. plist文件
  2. Document路徑
  3. 數據庫

由于保存的是大批量數據,且會不停的刷新新數據,因此應該選擇數據庫來存儲。使用數據庫可以快速地進行數據的讀取操作。

1.設計思路

如下圖,說明了離線緩存的流程:

  1. 當第一次打開app的時候,把從服務器獲取到的數據保存到沙盒中;
  2. 當下一次進入app的時候,首先從沙盒中找,如果沙盒中保存了之前的數據,則顯示沙盒中的數據;
  3. 如果沒有網絡,直接加載保存到沙盒中的數據。

2.實際應用

下面使用一個示例程序來介紹離線緩存。示例程序用到的框架有FMDB,SDWebImage,AFNetworking,數據是由聚合數據提供的開放API。

###JSON返回示例

{
    "resultcode": "200",
    "reason": "Success",
    "result": {
 "data": [
 {
 "id": "1001",
 "title": "糖醋小排",
 "tags": "浙菜;熱菜;兒童;酸甜;快手菜",
 "imtro": "糖醋小排,我估計愛吃的人太多了,要想做好這道菜,關鍵就是調料汁的配置,老抽不能放的太多,那樣顏色太重, 不好看,調料汁調好后,最好嘗一下,每個人的口味都會不同的,可以適當微調一下哈!",
 "ingredients": "肋排,500g",
 "burden": "蔥,適量;白芝麻,適量;鹽,3g;生粉,45g;料酒,30ml;雞蛋,1個;蔥,1小段;姜,3片;老抽,7ml;醋,30ml;白糖,20g;番茄醬,15ml;生抽,15ml;生粉,7g;姜,適量",
 "albums": [
 "http://img.juhe.cn/cookbook/t/1/1001_253951.jpg"
 ],
 "steps": [
 {
 "img": "http://img.juhe.cn/cookbook/s/10/1001_40ec58177e146191.jpg",
 "step": "1.排骨剁小塊,用清水反復清洗,去掉血水"
 },
 {
 "img": "http://img.juhe.cn/cookbook/s/10/1001_034906d012e61fcc.jpg",
 "step": "2.排骨放入容器中,放入腌料,攪拌均勻,腌制5分鐘"
 },
 {
 "img": "http://img.juhe.cn/cookbook/s/10/1001_b04cddaea2a1a604.jpg",
 "step": "3.鍋中放適量油,燒至5成熱,倒入排骨,炸至冒青煙時撈出,關火,等油溫降至五成熱時,開火,再次放入排骨,中火炸至焦黃、熟透撈出"
 },
 {
 "img": "http://img.juhe.cn/cookbook/s/10/1001_56b92264df500f01.jpg",
 "step": "4.鍋中留少許底油,放入蔥花、姜片爆香"
 },
 {
 "img": "http://img.juhe.cn/cookbook/s/10/1001_d78c57536a08dc4b.jpg",
 "step": "5.放入適量炸好的排骨,倒入調料汁,煮至湯汁濃稠時,關火,撒入蔥花、白芝麻點綴即可"
 }
 ]
 }
 ],
 "totalNum": 1,
 "pn": 0,
 "rn": 1
 },
    "error_code": 0
}

</div>

在SQLiteManager.m中

單例:

// 單例
+(SQLiteManager *)sharedInstance {
    @synchronized(self) {
        if (shareObj == nil) {
            shareObj = [[self alloc] init];
        }
    }
    return shareObj;
}

</div>

數據庫初始化:

// 初始化數據庫
-(instancetype)init {
    if (self = [super init]) {
        //文件路徑
        NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"step.sqlite"];
        //初始化數據庫
        self.database = [FMDatabase databaseWithPath:path];
        //打開數據庫
        [self.database open];
        if ([self.database open]) {
            //將step采用blob類型來存儲
            NSString *create = @"CREATE TABLE IF NOT EXISTS t_step(id integer PRIMARY KEY, step blob NOT NULL);";
            [self.database executeUpdate:create];
        }
    }
    return self;
}

</div>

從數據庫獲取數據:

//從數據庫獲取數據
-(NSArray *)stepsFromSqlite {
    NSString *sql = @"SELECT * FROM t_step";
    FMResultSet *set = [self.database executeQuery:sql];
    NSMutableArray *steps = [NSMutableArray array];
    while (set.next) {
        NSData *data = [set objectForColumnName:@"step"];
        Steps *step = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        [steps addObject:step];
    }
    return steps;
}

</div>

保存數據到數據庫:

// 保存數據到數據庫
-(void)saveSteps:(Steps *)step {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:step];
    [self.database executeUpdateWithFormat:@"INSERT INTO t_step(step) VALUES (%@);", data];
}

</div>

MenuTableViewController.m中

獲取服務器數據:

//獲取服務器數據
-(void)getData {
    AFHTTPSessionManager *session = [[AFHTTPSessionManager alloc] init];
    NSString *url = @"http://apis.juhe.cn/cook/queryid";
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    params[@"id"] = self.menuID;
    params[@"key"] = self.appKey;
    params[@"dtype"] = self.dtype;
    //從數據庫獲取數據
    NSArray *steps = [[SQLiteManager sharedInstance] stepsFromSqlite];
    if (steps.count) {
        self.stepsArray = [NSMutableArray arrayWithArray:steps];
    } else {
        //get請求,從服務器獲取數據
        [session GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSDictionary *result = responseObject[@"result"];
            NSArray *data = result[@"data"];
            for (NSDictionary *dict in data) {
                Menu *menu = [[Menu alloc] initWithDict:dict];
                self.stepsArray = menu.steps;
            }
            [self.tableView reloadData];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"error=%@",error);
        }];
    }
}

</div>

3.清除圖片

如下圖:

SDImageCache中提供了獲取當前緩存大小和清除緩存的的方法。

MenuTableViewController.m中

獲取當前緩存大小:

//字節大小
int byteSize = (int)[SDImageCache sharedImageCache].getSize;
//M大小
CGFloat cacheSize = byteSize / 1000.0 / 1000.0;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"清理緩存" message:[NSString stringWithFormat:@"緩存大小%.1fM",cacheSize] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"確定", nil];
[alert show];

</div>

清除緩存:

//清除緩存
[[SDImageCache sharedImageCache] clearDisk];

</div>

4.文件操作

使用SDImageCache可以清除圖片的緩存,但是有些緩存并不是圖片緩存,例如用戶臨時看的視頻文件或mp3文件,如果想要清除這些文件,就要使用文件操作的方法,遍歷沙盒中的Library/Cache文件夾,自己算出緩存文件夾的大小,把所有緩存文件清除。

注:文件夾是沒有大小的,只有文件有大小屬性。
//計算當前文件夾的大小
-(NSInteger)cachesFileSize {
    //文件管理者
    NSFileManager *mgr = [NSFileManager defaultManager];
    //判斷是否為文件
    BOOL dir = NO;
    BOOL exists = [mgr fileExistsAtPath:self isDirectory:&dir];
    if (!exists) return 0;//說明文件或文件夾不存在
    if (dir) { //self是一個文件夾
        //遍歷caches里面的內容 -- 直接和間接內容
        NSArray *subpaths = [mgr subpathsAtPath:self];
        NSInteger totalBytes = 0;
        //如果self是一個文件夾,則遍歷該文件夾下的文件
        for (NSString *subpath in subpaths) {
            //獲得全路徑
            NSString *fullpath = [self stringByAppendingPathComponent:subpath];
            BOOL directory = NO;
            [mgr fileExistsAtPath:fullpath isDirectory:&directory];
            if (!directory) { // self不是文件夾,計算文件的大小
                totalBytes += [[mgr attributesOfItemAtPath:fullpath error:nil][NSFileSize] integerValue];
            }
        }
        return totalBytes;
    } else { //self是一個文件
        return [[mgr attributesOfItemAtPath:self error:nil][NSFileSize] integerValue];
    }
}

</div>

如果覺得我寫的可以的話,請給我一個star,非常感謝!:smile::smile:

</div>

 本文由用戶 MarMebane 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!