- 每一个APP的设置里,我想都会有一条是和存储相关的。在现在的APP设计中,图片、动画、声音等浏览之后并不会立即删除,因为这第一是浪费流量(当然wifi就不存在了),第二是浪费时间,尤其是网速慢得用户,用户体验就相当不好了,而APP缓存的内容可以根据用户需求进行删除。
- 沙盒机制:iPhone是没有内存卡这个东西的,存储的地方叫做沙盒,关于沙盒的介绍
http://blog.csdn.net/zhenzhenzhao12/article/details/8162793
,这个网址中介绍的非常清楚,我在之前结合一些代码也有做比较详细的分析。就直接拿过来用,不另外总结了。
归档
-
什么叫归档:
- 持久化的意思,用某种格式保存一个对象叫做归档。
-
应用沙盒:每个应用都有自己的文件夹,互不干扰,保障安全性。
- 资源包:图片资源,plist资源。
- Docunments
- Library
- Caches
- Preferences
- tmp
-
沙盒位置:前往->资源库->Developer->CoreSimulator->Devices
- 可以在自己的应用中打印其路径:
NSLog(@"沙盒路径:%@",NSHomeDirectory());
- 可以在自己的应用中打印其路径:
- Documents
- 需要保存由
应用程序本身
产生的需要持久化文件或者数据,iTunes同步设备时会备份该目录。例如:游戏进度、涂鸦软件的绘图。 - 目录中的文件会被自动保存在iCloud、iTunes.
- 注意:不要保存从网路上下载的文件,否则会无法上架。
- 需要保存由
- 01Plist文件存储
- plist存储
- 将plist文件存入沙盒中,再从沙盒中读取。保存在Documents中。
- 如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型。就可以用writeToFile直接写到属性列表文件中。
// 地址拼接
NSString *docData = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
// 创建数据
NSArray *array = @[@10,@"name",@"picture"];
// 获取最终地址
NSString *data = [docData stringByAppendingPathComponent:@"data.plist"];
// 存
[array writeToFile:data atomically:YES];
// 地址拼接
NSString *docData = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
// 获取最终地址
NSString *data = [docData stringByAppendingPathComponent:@"data.plist"];
NSArray *array = [NSArray arrayWithContentsOfFile:data];
- 02 对象的存储 archive(归档)
- 什么是归档
- 这种方式可以对字符串、数字等进行归档,当然也可以对NSArray与NSDictionary进行归档。可以归档多个对象。可以对自定义对象进行归档。
- (1)单一简单操作
// 归档
NSString *homeDictionary = NSHomeDirectory();//获取根目录
NSString *homePath = [homeDictionary stringByAppendingPathComponent:@"atany.archiver"];//添加储存的文件名
BOOL flag = [NSKeyedArchiver archiveRootObject:@”归档” toFile:homePath];//归档一个字符串
// 接档
[NSKeyedUnarchiver unarchiveObjectWithFile:homePath]
- (2)多对象归档:使用encodeXXX方法进行归档,最后通过writeToFile方法写入文件。
//准备数据
CGPoint point = CGPointMake(1.0, 2.0);
NSString *info = @"坐标原点";
NSInteger value = 10;
NSString *multiHomePath = [NSHomeDirectory() stringByAppendingPathComponent:@"multi.archiver"];
NSMutableData *data = [[NSMutableData alloc]init];
NSKeyedArchiver *archvier = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
//对多个对象进行归档
[archvier encodeCGPoint:point forKey:@"kPoint"];
[archvier encodeObject:info forKey:@"kInfo"];
[archvier encodeInteger:value forKey:@"kValue"];
[archvier finishEncoding];
[data writeToFile:multiHomePath atomically:YES];
- 接档:从路径中获得数据构造NSKeyedUnarchiver实例,使用decodeXXXForKey方法获得文件中的对象。
NSMutableData *dataR = [[NSMutableData alloc]initWithContentsOfFile:multiHomePath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:dateR];
CGPoint pointR = [unarchiver decodeCGPointForKey:@"kPoint"];
NSString *infoR = [unarchiver decodeObjectForKey:@"kInfo"];
NSInteger valueR = [unarchiver decodeIntegerForKey:@"kValue"];
[unarchiver finishDecoding];
NSLog(@"%f,%f,%@,%d",pointR.x,pointR.y,infoR,valueR);
- (3)自定义对象进行归档
- 自定义对象实现了两个委托方法1)encodeWithCoder: 2)initWithCoder:
- 1)encodeWithCoder
Encodes the receiverusing a given archiver
// 通过一个给定的archiver把消息接收者进行编码。当接收到encodeObject消息的时候,类终端encodeWithCoder方法被调用。
- 2)initWithCoder
Returns an objectinitialized from data in a given unarchiver. (required)
// 从一个给定unarchiver的数据中返回一个初始化对象。
- Library
- 01 Caches
- 保存临时文件,
后续需要使用
,例如:缓存图片,离线数据(地图数据)。 - 系统不会清理cache目录中的文件。
- 要求程序开发时,必须提供Caches目录的清理解决方案。
- 保存临时文件,
- 02 Preferences
- 保存应用的所有偏好设置,使用NSUserDefault直接读写。(账号密码)。iTunes会同步设备时会备份该目录。
- 如果要想数据及时写入磁盘,还需要调用一个同步方法。
- 偏好设置一般是利用键值对保存,本质上也是plist文件,但是不用去找寻地址,有对应的文件夹,对应的方法查询地址,保存。
// 存储
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"jack" forKey:@"name"];
[defaults setBool:YES forKey:@"isOk"];
// 同步
[defaults synchronize];
// 读取
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:@"name"];
NSLog(@"%@",name);
- tmp
- 保存临时文件,
后续不需要使用
一般缓存比较大的,不重要的数据。 - tmp目录中的文件,系统会自动清理。
- 重新启动手机,tem目录会被清空。
- 系统磁盘空间不足时,系统会自动清理。
- 保存临时文件,
-
SQLite3(数据库存储)
- 存储效率最高。(封装了很多查询、删除的算法)
- 是关系型数据库。需要使用数据库语句。
-
清除缓存
-
界面处理。
- 01分析:这个界面的处理是非常简单的,因为我这里只搭建了一个tableViewCell。可以明显的感觉到tableViewCell和一般的设置界面的cell不一样,所以这个cell可以单独认证一个ID,其他的cell进行循环利用时就可以跳过这个cell了。
- 02搭建:实现tabelView的数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CXLClearCacheCell *cell = [tableView dequeueReusableCellWithIdentifier:setId];
return cell;
}
03.Cell的逻辑处理。
cell中在变化的就是存储的值,点击按钮时,还需要清除缓存。所以cell需要计算,那么在这里可以自定义cell,在自定义方法中计算缓存的大小。在点击清除缓存时,是有创建小菊花的,只是缓存的值不大,清除太快了就显示不出来了。
初始化方法中添加小菊花
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
// 添加菊花
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
// 开始动画
[activityIndicator startAnimating];
[self addSubview:activityIndicator];
self.loadingView = activityIndicator;
self.textLabel.text = @"清除缓存";
[self getCacheSize];
}
return self;
}
- 布局textLabel和菊花位置。
// 布局
- (void)layoutSubviews
{
[super layoutSubviews];
[self.textLabel sizeToFit];
self.textLabel.centerY = self.height*0.5;
self.loadingView.x = CGRectGetMaxX(self.textLabel.frame) + CXLMargin;
self.loadingView.centerY = self.height*0.5;
}
-
计算缓存大小。这个计算方法会在很多时候用到,所以我直接创建了一个分类来计算,这样在cell中直接调用就可以了。
- 在字符串分类中暴露两个方法:1个是计算大小,1个是根据大小分类。
@interface NSString (Extension)
- (NSInteger)fileSize;
- (NSString *)fileSizeString;
@end
- 方法实现
- (NSInteger)fileSize
{
// 创建文件管理者
NSFileManager *manager = [NSFileManager defaultManager];
// 先判断文件是否存在
BOOL isDirectory = NO;
BOOL exist = [manager fileExistsAtPath:self isDirectory:&isDirectory];
// 如果文件不存在
if (exist==NO) return 0;
// 如果文件存在 判断为文件夹还是文件
if (isDirectory) {
// 计算文件大小。拿到了完整路径
NSInteger fileSize = 0;
// 用完整路径查询所有的文件的大小,返回为数组
NSArray *pathArray = [manager subpathsAtPath:self];
// 遍历数组,将获得的数组字符串拼接到file上
for (NSString *path in pathArray) {
// 获得完整地址
NSString *fulSubPath = [self stringByAppendingPathComponent:path];
// 获得属性
NSDictionary *attrs = [manager attributesOfItemAtPath:fulSubPath error:nil];
// 过滤掉文件夹
if ([attrs[NSFileType] isEqualToString:NSFileTypeDirectory]) continue;
// 将属性中的fileSize相加。
fileSize += [attrs[NSFileSize] integerValue];
}
return fileSize;
}
return [[manager attributesOfItemAtPath:self error:nil][NSFileSize] integerValue];
}
- (NSString *)fileSizeString
{
NSInteger fileSize = self.fileSize;
// 设定一个单位
CGFloat unit = 1000.0;
// 对fileSize进行判断
if (fileSize>= unit * unit * unit) {
return [NSString stringWithFormat:@"%.1fGB",fileSize/(unit*unit*unit)];
}else if (fileSize>= unit*unit) {
return [NSString stringWithFormat:@"%.1fMB",fileSize/(unit*unit)];
}else if (fileSize>= unit){
return [NSString stringWithFormat:@"%.1fKB",fileSize/unit];
}else
{
return [NSString stringWithFormat:@"%zdB",fileSize];
}
}
- 在cell中调用.我采用的是异步,毕竟如果缓存非常大的话,计算也是比较耗时的,所以尽量不要占用主线程的时间。
// 获取缓存大小
- (void)getCacheSize
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 应用沙盒地址,返回的是一个数组
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// 拼接路径
NSString *file = [caches stringByAppendingPathComponent:@"default/com.hackemist.SDWebImageCache.default"].fileSizeString;
// 计算缓存大小只是为了在label中显示。
NSString *str = [NSString stringWithFormat:@"清除缓存(%@)",file];
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程
self.textLabel.text = str;
[self.loadingView removeFromSuperview];
});
});
}
- 在选中状态时清除缓存。在这里我直接用的SDWebImage中的清楚缓存的方法,如果你不想用到这个框架,可以利用一个异步方法,将fileManager清除。
- 01利用一个异步函数,取出沙盒中的数据,remove掉。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSString *cachPath = [ NSSearchPathForDirectoriesInDomains ( NSCachesDirectory , NSUserDomainMask , YES ) objectAtIndex : 0 ];
NSArray *files = [[ NSFileManager defaultManager ] subpathsAtPath :cachPath];
for ( NSString *p in files) {
NSError *error;
NSString *path = [cachPath stringByAppendingPathComponent :p];
if ([[ NSFileManager defaultManager ] fileExistsAtPath :path]) {
[[ NSFileManager defaultManager ] removeItemAtPath :path error :&error];
}
}dispatch_async(dispatch_get_main_queue(), ^{
// 创建cell
CXLClearCacheCell *cell = (CXLClearCacheCell *) [tableView cellForRowAtIndexPath:indexPath];
// 取消遮盖
[SVProgressHUD showSuccessWithStatus:@"清除缓存成功"];
// 在cell中写的一个删除后的方法
[cell regist];
});
- 02利用SDWebImage只需要调用删除缓存方法就直接删除。
// 清除缓存代码
[[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
// 创建cell
CXLClearCacheCell *cell = (CXLClearCacheCell *) [tableView cellForRowAtIndexPath:indexPath];
// 取消遮盖
[SVProgressHUD showSuccessWithStatus:@"清除缓存成功"];
[cell regist];
}];