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
}

在SQLiteManager.m中

单例:

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

数据库初始化:

// 初始化数据库
-(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;
}

从数据库获取数据:

//从数据库获取数据
-(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;
}

保存数据到数据库:

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

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);
        }];
    }
}

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];

清除缓存:

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

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];
    }
}

如果觉得我写的可以的话,请给我一个star,非常感谢!😄😄

Demo地址

github地址

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

推荐阅读更多精彩内容

  • 移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebIm...
    爱易寒曲易散阅读 1,362评论 0 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,791评论 25 707
  • 转载:H5缓存机制浅析-移动端Web加载性能优化【干货】 作者:贺辉超,腾讯游戏平台与社区产品部 高级工程师 目录...
    meng_philip123阅读 11,495评论 6 48
  • 有天,在路上 遇到了你 你说,你在奔跑 为了幸福和美好 我问你,什么是美好 你仰望星空,沉默不语 我不知道 是眼前...
    阳光一风筝阅读 273评论 0 0
  • 眼前白茫茫一片,什么也看不清。 雪已经停了,风却还很大。戴上口罩咳个不停的眼镜先生的镜片起了雾,只能让西装先生搀着...
    A_venir阅读 244评论 0 0