今天想来聊聊iOS的数据持久化。因为今天在家辅导表弟,玩iPad的时候,发现XX应用没有对首页做缓存,正好家里网断了(初三小表弟,你懂得),手机共享伤不起。
回归正题,我建议凡是数据比较多的应用,你都要考虑做下缓存,我看今日头条就不错,在网好的时候,它除了新闻的图片不提前缓存,所有首页文字信息都提前缓存下来了,看的很舒服。
iOS数据持久化,无非几种。
分开讲吧,这篇讲,归档吧。(Archiver持久化)
我推荐一个关于Archive的博文,很详细,这里就不多做说明了。点击打开链接
基本的数据类型如NSString、NSDictionary、NSArray、NSData、NSNumber等可以用属性列表的方法持久化到.plist 文件中,但如果是一些自定义的类的话,属性列表的方法就不管用了。archiver 方法可以做到。
这里强调下,NSKeyedArchive 并不是所有对象都可以通过它来归档的,我们想要归档的话,就必须要遵守NSCoding协议
遵守协议是第一步,之后在遵守协议,希望归档的对象实现两个协议方法,就可以实现数据归档了。
一、 encodeWithCoder:(NSCoder *)encoder 代理方法
-(void)encodeWithCoder:(NSCoder *)encoder{
[super encodeWithCoder:encoder];//不要忘了这个 首先要调用父类的方法,NSObject除外,NSObject 没有实现此方法。
[encoder encodeInt:self.age forKey:@"age"];
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeFloat:self.height forKey:@"height"];
}
二、initWithCoder:(NSCoder *)decoder 代理方法
-(id)initWithCoder:(NSCoder *)decoder{
self = [super initWithCoder:decoder];//不要忘了这个
self.age = [decoder decodeIntForKey:@"age"];
self.name = [decoder decodeObjectForKey:@"name"];
self.height = [decoder decodeFloatForKey:@"height"];
return self;
}
之后的操作就是在归档的地方归档在想解档的地方解档
//创建 -(void)createPerson{ person *p = [[[person alloc] init] autorelease]; p.age = 20; p.name = @"Rio"; p.height =1.75f; //获得Document的路径 NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"];//拓展名可以自己随便取 [NSKeyedArchiver archiveRootObject:p toFile:path]; } //读取 -(void)readPerson{ NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [documents stringByAppendingPathComponent:@"person.archiver"]; person *person1 = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; NSLog(@"%@",person1); }
之后,我之前做一款应用时,就用到过这方面来处理缓存
把原理和源码也放在这里了,
原理就是归档,通过请求数据的URL来命名归档的文件名,加上MD5加密一下,基本保证了文件名不重复,
MD5 加密如下,我的处理是给NSString加上了一个类别。
// // ScienceCacheManager.m // 每日新鲜事 // // Created by JackYang on 15/9/1. // Copyright (c) 2015年 JackYang. All rights reserved. // #import "NSString+Hashing.h" #import <CommonCrypto/CommonDigest.h> @implementation NSString (NSString_Hashing) - (NSString *)MD5Hash { const char *cStr = [self UTF8String]; unsigned char result[16]; CC_MD5(cStr, strlen(cStr), result); return [NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]]; } @end
剩下的代码就比较通用了。
我都放在这里了。
// // LimitCachManager.h // 每日新鲜事 // // Created by JackYang on 15/9/16. // Copyright (c) 2015年 JackYang. All rights reserved. // #import <Foundation/Foundation.h> @interface LimitCachManager : NSObject //保存url 对应的数据 + (void)saveData:(id)object atUrl:(NSString*)url; //读取url对应的数据 + (id)readDataAtUrl:(NSString*)ulr; //判断缓存数据是否有效 + (BOOL)isCacheDataInvalid:(NSString*)url; //计算缓存的大小 + (NSInteger)cacheSize; //清除缓存 + (void)clearDisk; @end
主要实现
#import "LimitCachManager.h" #import "NSString+Hashing.h" @implementation LimitCachManager //得到本地缓存的目录 + (NSString*)cacheDirectory { //得到沙盒目录下的cache文件夹 NSString *cachDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; cachDir = [cachDir stringByAppendingPathComponent:@"LimitCache"]; //创建LimitCache文件夹,目的是把所有的缓存数据放到该文件夹下面 //attribute:nil只文件夹跟父文件夹一样的读写属性 NSError *error; BOOL bret = [[NSFileManager defaultManager] createDirectoryAtPath:cachDir withIntermediateDirectories:YES attributes:nil error:&error]; if (!bret) { NSLog(@"%@",error); return nil; } return cachDir; } + (NSString*)cacheFileFullPath:(NSString*)url { //得到保存的文件的全路径,使用url的MD5加密得到的字符串作为文件名 //这样url和文件名就对应起来了 //[url MD5Hash] 是把url进行MD5加密得到的字符串 //MD5 加密算法是不可逆的 NSString *fileName = [url MD5Hash]; NSString *cacheDir = [self cacheDirectory]; return [cacheDir stringByAppendingPathComponent:fileName]; } //保存url 对应的数据 //输入的数据,要么是字典,要么数组 + (void)saveData:(id)object atUrl:(NSString*)url{ //首先得到保存的文件路径 NSString *fileFullPath = [self cacheFileFullPath:url]; //写入数据,使用NSKeyedArchiver进行数据转换 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object]; [data writeToFile:fileFullPath atomically:YES]; } //读取url对应的数据 + (id)readDataAtUrl:(NSString*)url { NSString *fileFullPath = [self cacheFileFullPath:url]; NSData *data = [NSData dataWithContentsOfFile:fileFullPath]; return [NSKeyedUnarchiver unarchiveObjectWithData:data]; } //判断缓存数据是否有效 + (BOOL)isCacheDataInvalid:(NSString*)url { //isDirectory 的参数是返回给我们是否是一个目录 NSString *fileFullPath = [self cacheFileFullPath:url]; BOOL isFileExist = [[NSFileManager defaultManager] fileExistsAtPath:fileFullPath isDirectory:nil]; //获取文件的属性 NSDictionary *attributeDic = [[NSFileManager defaultManager] attributesOfItemAtPath:fileFullPath error:nil]; NSDate *lastModify = attributeDic.fileModificationDate; NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:lastModify]; BOOL isExpire = (timeInterval > 60*60); if (isFileExist && !isExpire) { return YES; } return NO; } //计算缓存的大小,遍历缓存目录,把文件内容大小累加 + (NSInteger)cacheSize { NSInteger totalSize = 0; NSString *cacheDir = [self cacheDirectory]; //得到目录的枚举器,使用它来枚举目录下的所有文件 NSDirectoryEnumerator *enmuerator = [[NSFileManager defaultManager]enumeratorAtPath:cacheDir]; for (NSString *fileName in enmuerator) { NSString *fileFullPath = [cacheDir stringByAppendingPathComponent:fileName]; NSDictionary *attributeDic = [[NSFileManager defaultManager] attributesOfItemAtPath:fileFullPath error:nil]; totalSize += attributeDic.fileSize; } return totalSize; } //清除缓存 + (void)clearDisk { NSString *cacheDir = [self cacheDirectory]; [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:nil]; } @end
当使用是就调用,传入url 存储,读取时先判断文件存在不存在,不存在,就获取,存在就读取本地数据。
然后在清除缓存时,将此缓存一并清除。