本文部分内容摘录自:人魔七七:http://www.cnblogs.com/qiqibo/
缓存分为两种:1:按需缓存,2:预缓存。
第一种是**按需缓存**,这种情况下应用缓存起请求应答,就和Web浏览器的工作原理一样;按需缓存工作原理类似于浏览器缓存。它允许我们查看以前查看或者访问过的内容。按需缓存可以通过在打开一个视图控制器时按需地缓存数据模型(创建一个数据模型缓存)来实现,而不是在一个后台线程上做这件事。
第二种是**预缓存**,这种情况是缓存全部内容(或者最近n条记录)以便离线访问。例如:网易新闻的离线阅读
在众多可以本地保存数据的技术中,有三种脱颖而出:URL缓存、数据模型缓存(利用NSKeyedArchiver)和Core Data。
假设你正在开发一个应用,需要缓存数据以改善应用表现出的性能,你应该实现按需缓存(使用数据模型缓存或URL缓存)。另一方面,如果需要数据能够离线访问,而且具有合理的存储方式以便离线编辑,那么就用高级序列化技术(如Core Data)。
URL缓存、数据模型缓存(利用NSKeyedArchiver)适用于 -- 按需缓存
Core Data或者SQLite 适用于 -- 预缓存
---------以下为编者自己的见解----------------
两种缓存的实现方式:
按需缓存: a:可以通过对URL地址编码,设计一个单例,这个单例中管理着一个字典,字典格式为dic={[url md5]:"过期日期"},每次请求都将请求的NSData数据保存到沙盒目录,下次请求时,先去字典中查找是否有此键值,有的话,在判断是否已经过期,如果过期再去请求。b:方式是一样的,只是不使用单例,而是直接使用数据库,存储的信息也是字典中的那种格式。
按需缓存有两个优点:1:缓解服务器压力,2:节省用户流量。当然它的缺点就是不能及时的让用户获取及时的信息。当然你可以通过设置过期日期来缓解。
预缓存:可以将每次请求下得NSData,解析成模型数组后,将每个模型保存到数组库中。每次View加载ViewDidLoad方法中先从数据库中读取数据,在ViewWillApperence 中请求数据。预缓存就是用来实现离线缓存的。通常我们所说的缓存就是离线缓存,但是根据需要可以两种方式结合使用。
代码:
第一种:按需缓存
//
// AZCacheSignal.h
// AZSMZDM
//
// Created by AndrewZhang on 14-12-19.
// Copyright (c) 2014年 AndrewZhang. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSString+Hashing.h"
@interface AZCacheSignal : NSObject
@property (nonatomic,assign)NSUInteger cacheSize;
#pragma mark -- 对外接口
+(instancetype)defaultCache;
#pragma mark -- 使用 Url + NSData的缓存方式
/**
Url缓存,第一个参数:url地址,第二个参数:多少秒过期
*/
-(void)setCacheUrlStr:(NSString *)string andTimeOut:(NSTimeInterval)time andData:(NSData *)data;
/**
得到缓存字典,外界将请求地址url经过MD5编码,然后遍历这个字典。看是否找到有这个键,如有没有,则访问url,如果有,则判断当前系统时间是否已经超过了过期时间,如果超过了,则请求url,如果咩有超过,则不请求,直接从 取出NSData。
*/
-(NSDictionary *)getCacheDic;
@end
//
// AZCacheSignal.m
// AZSMZDM
//
// Created by AndrewZhang on 14-12-19.
// Copyright (c) 2014年 AndrewZhang. All rights reserved.
//
#import "AZCacheSignal.h"
@interface AZCacheSignal()
@property (nonatomic,strong)NSString *path;
@property (nonatomic,strong)NSString *dataPath;
@end
@implementation AZCacheSignal
-(instancetype)init
{
if (self=[super init])
{
self.cacheSize=0;
}
return self;
}
-(void)setCacheSize:(NSUInteger)cacheSize
{
_cacheSize=_cacheSize+cacheSize;
}
+(instancetype)defaultCache
{
static AZCacheSignal *_cache=nil;
@synchronized(self)//线程安全
{
if (!_cache) {
_cache=[[AZCacheSignal alloc] init];
}
}
return _cache;
}
#pragma mark -- 可以在此处修改缓存字典保存的沙盒路径
-(NSString *)path
{
if (!_path) {
_path=[NSString stringWithFormat:@"%@/Library/Caches/AZMyAppCacheDic.plist",NSHomeDirectory()];
}
return _path;
}
#pragma mark --可以在此处修改 NSData 保存的沙盒路径
/**
Url缓存,第一个参数:url地址,第二个参数:多少秒过期
设计字典 dic={“urlMD5加密”:“过期时间”};
保存字典到沙盒目录中的 Library/Caches/AZMyAppCacheDic.plist
保存NSData到沙盒目录中的 tmp/
*/
-(void)setCacheUrlStr:(NSString *)string andTimeOut:(NSTimeInterval)time andData:(NSData *)data;
{
NSDate *passDate=[NSDate dateWithTimeInterval:time sinceDate:[NSDate date]];
NSFileManager* fm=[NSFileManager defaultManager];
if (![fm fileExistsAtPath:self.path])
{
NSDictionary *dic=[NSDictionary dictionary];
[dic writeToFile:self.path atomically:YES];
}
NSDictionary *cacheDic=[NSDictionary dictionaryWithContentsOfFile:self.path];
[cacheDic setValue:passDate forKey:[string MD5Hash]];
[cacheDic writeToFile:self.path atomically:YES];
NSString* dataPath=[NSString stringWithFormat:@"%@/tmp/%@",NSHomeDirectory(),[string MD5Hash]];
[data writeToFile:dataPath atomically:NO];
}
/**
得到缓存字典
*/
-(NSDictionary *)getCacheDic
{
NSFileManager* fm=[NSFileManager defaultManager];
if (![fm fileExistsAtPath:self.path])
{
NSDictionary *dic=[NSDictionary dictionary];
[dic writeToFile:self.path atomically:YES];
}
return [NSDictionary dictionaryWithContentsOfFile:self.path];
}
@end
第二种:预缓存
预缓存使用数据库,推荐使用CoreData,但是这里因为我已经建好了数据模型,所以使用了FMDB。
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
@interface AZDataBase : NSObject
@property (nonatomic,strong)FMDatabase * db;
+(instancetype)shareDataBase;
//根据数组插入数据到数据库
-(void)insertShowItemByArray:(NSArray *)array andPage:(NSString *)page;
-(void)insertLunFanItemByArray:(NSArray *)array;
//删除page指定的所有数据
-(void)delShowItemByPage:(NSString *)page;
//全部删除轮番数据
-(void)delLunFan;
//查询数据
-(NSArray *)selectShowItemByPage:(NSString *)page;
-(NSArray *)selectLunFan;
@end
//
// AZDataBase.m
// AZSMZDM
//
// Created by AndrewZhang on 14-12-25.
// Copyright (c) 2014年 AndrewZhang. All rights reserved.
//
#import "AZDataBase.h"
#import "AZShowItem.h"
#import "AZLunFanModel.h"
@implementation AZDataBase
-(FMDatabase *)db
{
if (!_db) {
NSString *path=[NSString stringWithFormat:@"%@/Documents/data.db",NSHomeDirectory()];
//数据库操作类
_db=[[FMDatabase alloc] initWithPath:path];
BOOL res=[_db open];
if (!res)
{
NSLog(@"数据库打开失败");
}
else
{
//创建表
res=[_db executeUpdate:@"create table if not exists ShowItem(uid integer primary key autoincrement,article_channel_id,article_channel_name,article_id,article_url,article_title,article_price,article_format_date,article_link,article_link_type,article_pic,article_worthy,article_unworthy,article_collection,article_comment,article_mall,article_referrals,article_rzlx,article_filter_content,page)"];
if (!res) {
NSLog(@"创建表1失败");
}
res=[_db executeUpdate:@"create table if not exists LunFan(uid integer primary key autoincrement,banner_id,article_channel_id,article_id,banner_title,banner_pic,banner_url,banner_order,banner_type)"];
if (!res) {
NSLog(@"创建表2失败");
}
}
[_db close];
}
return _db;
}
+(instancetype)shareDataBase
{
static AZDataBase *_database=nil;
@synchronized(self)//线程安全
{
if (!_database) {
_database=[[AZDataBase alloc] init];
}
}
return _database;
}
//根据数组插入数据到数据库
-(void)insertShowItemByArray:(NSArray *)array andPage:(NSString *)page
{
[self.db open];
for (AZShowItem *showItem in array) {
[self.db executeUpdate:@"insert into ShowItem(article_channel_id,article_channel_name,article_id,article_url,article_title,article_price,article_format_date,article_link,article_link_type,article_pic,article_worthy,article_unworthy,article_collection,article_comment,article_mall,article_referrals,article_rzlx,article_filter_content,page)values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",showItem.article_channel_id,showItem.article_channel_name,showItem.article_id,showItem.article_url,showItem.article_title,showItem.article_price,showItem.article_format_date,showItem.article_link,showItem.article_link_type,showItem.article_pic,showItem.article_worthy,showItem.article_unworthy,showItem.article_collection,showItem.article_comment,showItem.article_mall,showItem.article_referrals,showItem.article_rzlx,showItem.article_filter_content,page];
}
[self.db close];
}
-(void)insertLunFanItemByArray:(NSArray *)array
{
[self.db open];
for (AZLunFanModel *lf in array) {
[self.db executeUpdate:@"insert into LunFan(banner_id,article_channel_id,article_id,banner_title,banner_pic,banner_url,banner_order,banner_type)values(?,?,?,?,?,?,?,?)",lf.banner_id,lf.article_channel_id,lf.article_id,lf.banner_title,lf.banner_pic,lf.banner_url,lf.banner_order,lf.banner_type];
}
[self.db close];
}
//删除page指定的所有数据
-(void)delShowItemByPage:(NSString *)page
{
[self.db open];
NSString *delStr=[NSString stringWithFormat:@"delete from ShowItem WHERE page='%@' ",page];
[self.db executeUpdate:delStr];
[self.db close];
}
//全部删除轮番数据
-(void)delLunFan
{
[self.db open];
NSString *delStr=[NSString stringWithFormat:@"delete from LunFan"];
[self.db executeUpdate:delStr];
[self.db close];
}
//查询数据
-(NSArray *)selectShowItemByPage:(NSString *)page
{
[self.db open];
NSString *selectStr=[NSString stringWithFormat:@"select * from ShowItem where page='%@' ",page];
FMResultSet *resultSet=[self.db executeQuery:selectStr];
NSMutableArray *temArray=[NSMutableArray array];
if (resultSet)
{
// 遍历结果集
while ([resultSet next]) {
AZShowItem *showItem=[[AZShowItem alloc] init];
showItem.article_channel_id=[resultSet stringForColumn:@"article_channel_id"];
showItem.article_channel_name=[resultSet stringForColumn:@"article_channel_name"];
showItem.article_id=[resultSet stringForColumn:@"article_id"];
showItem.article_url=[resultSet stringForColumn:@"article_url"];
showItem.article_title=[resultSet stringForColumn:@"article_title"];
showItem.article_price=[resultSet stringForColumn:@"article_price"];
showItem.article_format_date=[resultSet stringForColumn:@"article_format_date"]; showItem.article_link=[resultSet stringForColumn:@"article_link"];
showItem.article_link_type=[resultSet stringForColumn:@"article_link_type"]; showItem.article_pic=[resultSet stringForColumn:@"article_pic"];
showItem.article_worthy=[resultSet stringForColumn:@"article_worthy"];
showItem.article_unworthy=[resultSet stringForColumn:@"article_unworthy"];
showItem.article_collection=[resultSet stringForColumn:@"article_collection"];
showItem.article_comment=[resultSet stringForColumn:@"article_comment"];
showItem.article_mall=[resultSet stringForColumn:@"article_mall"];
showItem.article_referrals=[resultSet stringForColumn:@"article_referrals"];
showItem.article_rzlx=[resultSet stringForColumn:@"article_rzlx"]; showItem.article_filter_content=[resultSet stringForColumn:@"article_filter_content"];
[temArray addObject:showItem];
}
}
[self.db close];
return [temArray copy];
}
-(NSArray *)selectLunFan
{
[self.db open];
NSString *selectStr=[NSString stringWithFormat:@"select * from LunFan"];
FMResultSet *resultSet=[self.db executeQuery:selectStr];
NSMutableArray *temArray=[NSMutableArray array];
if (resultSet)
{
// 遍历结果集
while ([resultSet next]) {
AZLunFanModel *lf=[[AZLunFanModel alloc] init];
lf.banner_id=[resultSet stringForColumn:@"banner_id"];
lf.article_channel_id=[resultSet stringForColumn:@"article_channel_id"];
lf.article_id=[resultSet stringForColumn:@"article_id"];
lf.banner_title=[resultSet stringForColumn:@"banner_title"];
lf.banner_pic=[resultSet stringForColumn:@"banner_pic"];
lf.banner_url=[resultSet stringForColumn:@"banner_url"];
lf.banner_order=[resultSet stringForColumn:@"banner_order"];
lf.banner_type=[resultSet stringForColumn:@"banner_type"];
[temArray addObject:lf];
}
}
[self.db close];
return [temArray copy];
}
@end
预缓存的主要目的 就是在离线的情况下 打开应用程序不会出现空白一片。而是显示的缓存中的数据。这样可以提高用户体验。
图:
在离线下: