1 应用沙盒结构分析
沙盒文件的结构:(三个文件夹,Document,Library,tmp)
应用程序包:包含了所有的资源文件和可执行文件
Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录(NSUserDefault plist生成的文件)
2 应用数据存储的常用方式
XML属性列表(plist)归档 (NSUserDefault实质也是polist列表)
Preference(偏好设置)
NSKeyedArchiver归档(NSCoding)
SQLite3
NSKeyedArchiver归档(NSCoding)
SQLite3
Core Data
3 应用沙盒目录的常见获取方式
(1)NSString * appPath = [[NSBundle mainBundle] bundlePath]; //获取APP目录
(1)NSHomeDirectory() //沙盒路径
(2)tmp:NSTemporaryDirectory();//沙盒中temp文件夹路径
(3)document:
NSString*documentPath =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)[0];
/Users/hanting/Library/Developer/CoreSimulator/Devices/31AE3ADD-C1A4-4199-82AD-83901DB72620/data/Containers/Data/Application/D1F17CD8-D389-46DD-9AA6-41E924AE892D/Documents//一个模拟器路径
(4)Library/Caches:(跟Documents类似的2种方法)
利用沙盒根目录拼接”Caches”字符串
利用NSSearchPathForDirectoriesInDomains函数(将函数的参数改为:NSCachesDirectory即可)
利用NSSearchPathForDirectoriesInDomains函数(将函数的参数改为:NSCachesDirectory即可)
//
directory:获取哪个文件夹
// domainMask:在哪个范围内搜索,NSUserDomainMask:表示在用户的手机上查找
// expandTilde:是否展开全路径 YES:表示展开全路径 NO:不会展开全路径,会把应用沙盒的路径用波浪号(~)代替
// 获取到Caches文件夹路径
// domainMask:在哪个范围内搜索,NSUserDomainMask:表示在用户的手机上查找
// expandTilde:是否展开全路径 YES:表示展开全路径 NO:不会展开全路径,会把应用沙盒的路径用波浪号(~)代替
// 获取到Caches文件夹路径
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
//
拼接文件名
NSString *filePath = [cachePath stringByAppendingPathComponent:@"arr.plist"];
4 plist文件的存取.(一般存储在document,preference中)
(1)plist存储,生成一个plist文件.
(2)plist不是数组就是字典,plist存储就是用来存储字典或者数组.
(3)
Plist不能存储自定义对象,用NSUserDefault生成的文件会导出成plist文件,存储在
Library/Preference
(4)把一些系统自带的OC对象生成pilst文件存储起来.
(5) plist存储原理:
• 只要有writeToFile的对象,就能进行plist存储,调用writeToFile就能自动生成plist格式的文件。
• 一般常用的Foundation对象都有这个方法,数组,字典,字符串等
// 1、存储plist,File:文件的全路径
[arr writeToFile:filePath atomically:YES];
// 2、读取plist,之前是什么类型存储的,读取也是什么
// 2、读取plist,之前是什么类型存储的,读取也是什么
NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];
例如:
将一个NSDictionary对象归档到一个plist属性列表中
//
将数据封装成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject: @" 母鸡 " forKey: @" name " ];
[dict setObject: @" 15013141314 " forKey: @" phone " ];
[dict setObject: @" 27 " forKey: @" age " ];
// 将字典持久化到Documents/stu.plist文件中
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject: @" 母鸡 " forKey: @" name " ];
[dict setObject: @" 15013141314 " forKey: @" phone " ];
[dict setObject: @" 27 " forKey: @" age " ];
// 将字典持久化到Documents/stu.plist文件中
[dict writeToFile:path atomically:YES];
//只能写入一个dictionary.
取出这个plist文件:
NSDictionary*dic = [NSDictionarydictionaryWithContentsOfFile:path];
(6)偏好设置
很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能
偏好设置原理:不需要关心文件名,直接通过NSUserDefaults操作,默认就存到偏好设置里面了。
通过NSUserDefaults就能直接访问软件的偏好设置(Library/Preferences)
以字典的形式进行偏好设置,用法跟字典一样.
//表单中的Root
偏好设置好处: 1.不需要关心文件名 2.快速进行键值对存储 3.直接存储基本数据类型
保存:
// 获取单例
NSUserDefaults *defaults =
[NSUserDefaults standardUserDefaults];
// @"123" key:pwd
// @"123" key:pwd
[defaults setObject:@"123" forKey:@"pwd"];
//
bool
[defaults setBool:YES forKey:@"isOn"];
//
int
[defaults setInteger:10 forKey:@"num"];
读取也很简单
// 利用NSUserDefaults单例
NSString *pwd = [[NSUserDefaults standardUserDefaults] objectForKey:
@"
pwd
"
];
NSInteger i = [[NSUserDefaults standardUserDefaults] integerForKey:@"num"];
5 NSData(自定义对象:数据模型)存取
使用archiveRootObject:toFile:方法可以将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象
(1)归档:
// 新建一块可变数据区
NSMutableData *data = [NSMutableData data];
// 将数据区连接到一个NSKeyedArchiver对象
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
// 开始存档对象,存档的数据都会存储到NSMutableData中
[archiver encodeObject:person1 forKey:@"person1"];
[archiver encodeObject:person2 forKey:@"person2"];
// 存档完毕(一定要调用这个方法)
[archiver finishEncoding];
// 将存档的数据写入文件
NSMutableData *data = [NSMutableData data];
// 将数据区连接到一个NSKeyedArchiver对象
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
// 开始存档对象,存档的数据都会存储到NSMutableData中
[archiver encodeObject:person1 forKey:@"person1"];
[archiver encodeObject:person2 forKey:@"person2"];
// 存档完毕(一定要调用这个方法)
[archiver finishEncoding];
// 将存档的数据写入文件
[data writeToFile:path atomically:YES];
(2)反归档:
// 从文件中读取数据
NSData *data = [NSData dataWithContentsOfFile:path];
// 根据数据,解析成一个NSKeyedUnarchiver对象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
// 恢复完毕
[unarchiver finishDecoding];
NSData *data = [NSData dataWithContentsOfFile:path];
// 根据数据,解析成一个NSKeyedUnarchiver对象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
// 恢复完毕
[unarchiver finishDecoding];
(3)利用归档实现深复制
比如对一个Person对象进行深复制
// 临时存储person1的数据
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
// 解析data,生成一个新的Person对象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分别打印内存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0
// 解析data,生成一个新的Person对象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分别打印内存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0
(4)归档:存储自定义对象
为了把模型对象用
NSKeyedArchiver
归档,模型类需要遵循
NSCoding
协议。
NSCoding协议方法
当一个对象要归档时,会调用这个方法:
- (void)encodeWithCoder:(NSCoder *)aCoder;
当一个对象要反归档时,会调用这个方法:
- (id)initWithCoder:(NSCoder *)aDecoder;
然后归档调用这两个方法之一:
[NSKeyedArchiver archiveRootObject:objectForArchiving
toFile:archiveFilePath];
[NSKeyedArchiver archivedDataWithRootObject:objectForArchiving];
反归档调用这个方法:
[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
在model中实现归档方法:
// 什么时候调用:当一个对象要归档的时候就会调用这个方法归档
// 作用:告诉苹果当前对象中哪些属性需要归档
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInt:_age forKey:@"age"];
// 作用:告诉苹果当前对象中哪些属性需要归档
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInt:_age forKey:@"age"];
}
在model中实现反归档方法:
// 作用:告诉苹果当前对象中哪些属性需要解档
// initWithCoder什么时候调用:只要解析一个文件的时候就会调用
- (id)initWithCoder:(NSCoder *)aDecoder
{
#warning [super initWithCoder]
// 这里不能用[super initWithCoder],什么时候调用[super initWithCoder:aDecoder]:只要父类遵守了NSCoding协议,就调用
if (self = [super init]) {
// 解档
// 注意一定要记得给成员属性赋值
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}
// initWithCoder什么时候调用:只要解析一个文件的时候就会调用
- (id)initWithCoder:(NSCoder *)aDecoder
{
#warning [super initWithCoder]
// 这里不能用[super initWithCoder],什么时候调用[super initWithCoder:aDecoder]:只要父类遵守了NSCoding协议,就调用
if (self = [super init]) {
// 解档
// 注意一定要记得给成员属性赋值
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}
6 清理文件 (计算缓存大小,清除缓存)
(1)由于缓存文件存在沙箱中,我们可以通过NSFileManager API来实现对缓存文件大小的计算。
//计算单个文件大小
-(longlong)fileSizeAtPath:(NSString*)filePath{
NSFileManager* manager = [NSFileManagerdefaultManager];
if ([manager fileExistsAtPath:filePath]){
return [[manager attributesOfItemAtPath:filePatherror:nil]fileSize];
}
return 0;
}
//遍历计算文件夹大小,返回M
-(float)getCacheSizeAtPath:(NSString*)folderPath{
NSFileManager* manager = [NSFileManagerdefaultManager];
if (![manager fileExistsAtPath:folderPath])return0;
NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath]objectEnumerator];//从前向后枚举器
NSString* fileName;
long long folderSize = 0;
while ((fileName = [childFilesEnumerator nextObject]) !=nil){
NSLog(@"fileName ==== %@",fileName);
NSString* fileAbsolutePath = [folderPathstringByAppendingPathComponent:fileName];
NSLog(@"fileAbsolutePath ==== %@",fileAbsolutePath);
folderSize += [self fileSizeAtPath:fileAbsolutePath];
}
NSLog(@"folderSize ==== %lld",folderSize);
NSFileManager* manager = [NSFileManagerdefaultManager];
if ([manager fileExistsAtPath:filePath]){
return [[manager attributesOfItemAtPath:filePatherror:nil]fileSize];
}
return 0;
}
//遍历计算文件夹大小,返回M
-(float)getCacheSizeAtPath:(NSString*)folderPath{
NSFileManager* manager = [NSFileManagerdefaultManager];
if (![manager fileExistsAtPath:folderPath])return0;
NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath]objectEnumerator];//从前向后枚举器
NSString* fileName;
long long folderSize = 0;
while ((fileName = [childFilesEnumerator nextObject]) !=nil){
NSLog(@"fileName ==== %@",fileName);
NSString* fileAbsolutePath = [folderPathstringByAppendingPathComponent:fileName];
NSLog(@"fileAbsolutePath ==== %@",fileAbsolutePath);
folderSize += [self fileSizeAtPath:fileAbsolutePath];
}
NSLog(@"folderSize ==== %lld",folderSize);
return folderSize/(1024.0*1024.0);
(2) 删除文件
-(
void)clearCacheAtPath:(NSString*)path{
NSFileManager *fileManager=[NSFileManagerdefaultManager];
if ([fileManager fileExistsAtPath:path]) {
NSArray *childerFiles=[fileManager subpathsAtPath:path];
for (NSString *fileName in childerFiles) {
//如有需要,加入条件,过滤掉不想删除的文件
NSString *absolutePath=[path stringByAppendingPathComponent:fileName];
[fileManager removeItemAtPath:absolutePatherror:nil];
}
if ([fileManager fileExistsAtPath:path]) {
NSArray *childerFiles=[fileManager subpathsAtPath:path];
for (NSString *fileName in childerFiles) {
//如有需要,加入条件,过滤掉不想删除的文件
NSString *absolutePath=[path stringByAppendingPathComponent:fileName];
[fileManager removeItemAtPath:absolutePatherror:nil];
}
}
}
}
7 NSURLCache URL缓存
(1)工作原理:
第一次网络请求之后,当服务器返回数据时,需要做以下步骤
(
1
)使用服务器的数据(比如解析、显示)
(
2
)将服务器的数据缓存到沙盒
此时缓存的情况是:内存缓存中有数据,硬盘缓存中有数据。
再次请求数据分为两种情况:
( 1 )如果程序并没有被关闭,一直在运行
那么此时内存缓存中有数据,硬盘缓存中有数据。如果此时再次请求数据,直接使用内存缓存中的数据即可
再次请求数据分为两种情况:
( 1 )如果程序并没有被关闭,一直在运行
那么此时内存缓存中有数据,硬盘缓存中有数据。如果此时再次请求数据,直接使用内存缓存中的数据即可
(
2
)如果程序重新启动
那么此时内存缓存已经消失,没有数据,沙盒缓存依旧存在,还有数据。如果此时再次请求数据,需要读取内存中缓存的数据。
提示:从沙盒缓存中读取数据后,内存缓存中又有数据了
(2)使用的注意事项
1.
只能用在
get
请求里面,
post
可以洗洗睡了。
2.缓存机制选择 NSURLRequestReturnCacheDataElseLoad
有缓存从缓存取数据,没有缓存从网络取数据。
3.
需要服务器定义数据是否发生变化,
allHeaderFields
里可以查找到是否修改了的信息。公司服务器没有定义的话,就不能够判断读取的缓存数据是否需要刷新。
4.有缓存的时候就不会进行网络请求,会变动的数据就尽量不要用这种方式请求了。一般也应用于离线阅读,不会改变数据的地方。
5.删除缓存的removeCachedResponseForRequest这个方法是无效的.所以缓存是不会被删除的—只有删除全部缓存才有效
6.
如果大量使用缓存,会越积越大,建议定期清除缓存
(3)使用过程
常用方法:
(1
)获得全局缓存对象(没必要手动创建)
NSURLCache *cache = [NSURLCache sharedURLCache];
( 2 )设置内存缓存的最大容量(字节为单位,默认为 512KB ) - (void)setMemoryCapacity:(NSUInteger)memoryCapacity;
( 3 )设置硬盘缓存的最大容量(字节为单位,默认为 10M ) - (void)setDiskCapacity:(NSUInteger)diskCapacity;
( 4 )硬盘缓存的位置:沙盒 /Library/Caches
( 5 )取得某个请求的缓存 - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request;
( 6 )清除某个请求的缓存 - (void)removeCachedResponseForRequest:(NSURLRequest *)request;
( 2 )设置内存缓存的最大容量(字节为单位,默认为 512KB ) - (void)setMemoryCapacity:(NSUInteger)memoryCapacity;
( 3 )设置硬盘缓存的最大容量(字节为单位,默认为 10M ) - (void)setDiskCapacity:(NSUInteger)diskCapacity;
( 4 )硬盘缓存的位置:沙盒 /Library/Caches
( 5 )取得某个请求的缓存 - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request;
( 6 )清除某个请求的缓存 - (void)removeCachedResponseForRequest:(NSURLRequest *)request;
(
7
)清除所有的缓存
- (void)removeAllCachedResponses;
缓存
GET
请求
要想对某个
GET
请求进行数据缓存,非常简单
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置缓存策略
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
只要设置了缓存策略,系统会自动利用 NSURLCache 进行数据缓存
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置缓存策略
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
只要设置了缓存策略,系统会自动利用 NSURLCache 进行数据缓存
iOS对NSURLRequest提供了7种缓存策略:(实际上能用的只有4种)
NSURLRequestUseProtocolCachePolicy //默认的缓存策略(取决于协议)
NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求
NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求
NSURLRequestReturnCacheDataElseLoad// 有缓存就用缓存,没有缓存就重新请求
NSURLRequestReturnCacheDataDontLoad// 有缓存就用缓存,没有缓存就不发请求,当做请求出错处理(用于离线模式)
NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求
NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 未实现
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData // 忽略缓存,重新请求
NSURLRequestReturnCacheDataElseLoad// 有缓存就用缓存,没有缓存就重新请求
NSURLRequestReturnCacheDataDontLoad// 有缓存就用缓存,没有缓存就不发请求,当做请求出错处理(用于离线模式)
NSURLRequestReloadRevalidatingCacheData //未实现
(4)在caches中的存储
(5)代码示例
// 1.创建请求
NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:8080/YYServer/video"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2.设置缓存策略(有缓存就用缓存,没有缓存就重新请求)
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
// 3.发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@", dict);
}
}];
}
/**
//
定期处理缓存
if (缓存没有达到7天) {
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
}
//
获得全局的缓存对象
NSURLCache *cache = [NSURLCache sharedURLCache];
// if (
缓存达到
7
天
) {
// [cache removeCachedResponseForRequest:request];
// }
NSCachedURLResponse *response = [cache cachedResponseForRequest:request];
if (response) {
NSLog(@"---
这个请求已经存在缓存
");
} else {
NSLog(@"---
这个请求没有缓存
");
}