前言
在 iOS
开发过程中,经常需要 保存一些信息到本地
,那么就需要数据存储。
沙盒(sandbox)
每个应用程序都有自己的沙盒,且只能访问自己的沙盒
- 如何访问沙盒文件呢?
通过Xcode查看
- 滑动鼠标到
Installed Apps
的底部,左键点击设置按钮,选择 Download Container
,然后就可以将 沙盒文件
下载到本机上,查看所下载文件的内容即可
打开plist文件可查看设置
由上图可知 沙盒文件
下结构为:
应用程序包
1、Documents 持久化的数据
2、tmp 临时目录
3、Library
3.1、cache 缓存
3.2、Preference 偏好设置
- 沙盒文件下每个文件的具体作用?
沙盒路径的获取方法
- 沙盒的路径
用 NSHomeDirectory()
获取
1 2 3 4 5 6
| // 获取沙盒下的根目录 NSString * sandboxPath = NSHomeDirectory(); NSLog(@"%@",sandboxPath);
====== /var/mobile/Containers/Data/Application/F8CF4000-F78B-4511-996D-819B96F64389
|
- Documents 路径
1 2 3 4 5 6 7 8 9 10 11 12 13
| // 获取 Documents 路径 /* NSSearchPathForDirectoriesInDomains 返回绝对路径 NSDocumentDirectory 表示获取沙盒的Documents目录 NSUserDomainMask 表示是用户主目录 YES/NO 表示是否获取全路径 */ NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString * documentsPath = [paths lastObject]; NSLog(@"%@",documentsPath); ======= /var/mobile/Containers/Data/Application/52A513F9-96E3-4DB0-9A40-BE3403E0BD34/Documents
|
- Caches 路径
1 2 3 4 5 6 7
| // 获取Caches路径 NSArray * paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString * cachesPath = [paths lastObject]; NSLog(@"%@",cachesPath);
======= /var/mobile/Containers/Data/Application/A0FF5812-EC9C-404F-BF63-4F60FFB1DE01/Library/Caches
|
- tmp 路径
1 2 3 4 5 6
| // 获取tmp路径 NSString * tmpDir = NSTemporaryDirectory(); NSLog(@"%@",tmpDir);
========= /private/var/mobile/Containers/Data/Application/206388C3-5180-4631-8120-3CB10EBDA0D8/tmp/
|
常用的存储方式
- NSUserDefaults
- Plist
- 手动存放沙盒
- NSKeyedArchiver归档 / NSKeyedUnarchiver解档
- SQLite3
- FMDB
- CoreData
- keyChain 钥匙串
NSUserDefaults
NSUserDefaults
可以存储 OC基本类型(如:字符串、数组、字典等)
。但 不能存储自定义对象
,如果是 自定义对象,需要进行归档操作
。缓存数据到沙盒下 Library->Preference
。iTunes
同步设备时会 备份
1 2
| [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; [[NSUserDefaults standardUserDefaults] synchronize];
|
1
| [[NSUserDefaults standardUserDefaults] objectForKey:key];
|
1 2
| [[NSUserDefaults standardUserDefaults] removeObjectForKey:key]; [[NSUserDefaults standardUserDefaults] synchronize];
|
Plist
Plist
即 XML属性列表
,是将某些特定的类,通过XML文件的方式保存在 Documents
目录中
优点:这种方式 好处是 可视化
,可以 很直观
的看到文件的内容,同时 Xcode
提供了直接操作文件的功能。便于我们 增删改查
。
缺点:plist
文件一般作为固态的数据形式保存,对于经常改动的数据不好操作
- 在项目中直接创建
Plist
文件。
注意:
命名的时候不能用Info.plist、INfo.plist、xxxInfo.plist等形式,否则会与系统中存在的Info.plist文件发生冲突。
点击Root这一行,然后通过点击右键->Add Row或者点击Root后面的加号来增加一行。
这一行中包含三个属性,key、type、value。其中key是字段属性,type是字段类型,value是字段对应的值。
Type包含7中类型,对写入的数据结构应属于7仲:
(NSString,NSData,NSDate,NSNumber,NSArray,NSDictionary)
1 2 3 4 5 6 7 8 9 10 11 12 13
| // 从plist中直接读取代码 NSString * filePath = [[NSBundle mainBundle] pathForResource:@"InfoPlist" ofType:@"plist"]; NSDictionary * dic = [NSDictionary dictionaryWithContentsOfFile:filePath]; NSLog(@"%@",dic); ===== 2023-04-15 21:31:14.103498+0800 数据存储-plist[48688:9718159] { Name = "\U5f20\U5efa"; Other = ( 0, 1 ); }
|
- 代码创建
plist
文件,并 读写
数据,避免了在项目中创建 plist
文件导致不便更改的问题
1 2 3 4 5 6
| @interface ZJDataHelper : NSObject // 保存用户信息 + (BOOL)saveUserInfoInDocument:(NSDictionary *)dic; // 读取用户信息 + (NSMutableDictionary *)getUserInfoInDocument; @end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #import "ZJDataHelper.h"
#define DOCMENT_USER_PATH [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject] stringByAppendingPathComponent:@"UserData.plist"]
@implementation ZJDataHelper // 保存用户信息 + (BOOL)saveUserInfoInDocument:(NSDictionary *)dic{ BOOL isSuccess; NSString *document_path= DOCMENT_USER_PATH ; NSURL *fileUrl = [NSURL fileURLWithPath:document_path]; isSuccess = [dic writeToURL:fileUrl atomically:YES]; return isSuccess; } // 读取用户信息 + (NSMutableDictionary *)getUserInfoInDocument{ NSString *path= DOCMENT_USER_PATH; NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithContentsOfFile:path]; return dic; }
@end
|
-(BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically;
writeToURL 的好处是,既可以写入本地url也可以写入远程url,苹果推荐使用此方法写入plist文件
手动存放沙盒
沙盒和上面两种类似,只能存放 OC
基本数据,自定义对象不能直接存入。
1 2 3 4 5 6 7 8
| // 假设我们需往cache 存入数据,并命名为test的txt格式文件中 NSString *filePath = [cachesPath stringByAppendingPathComponent:@"test.txt"]; NSArray *dic = [[NSArray alloc] initWithObjects:@"name",@"zj" ,nil]; if([dic writeToFile:filePath atomically:YES]){ NSLog(@"存入成功"); } // 取出数据 打印 NSLog(@"%@",[NSArray arrayWithContentsOfFile:filePath]);
|
NSKeyedArchiver归档 / NSKeyedUnarchiver解档
之前说,不管是 NSUserDefaults
或者是 plist
都不能对 自定义的对象
进行存储, OC提供了 解归档
恰好解决这个问题。 解归档针对的是一个对象,假设我们现在有一个 Person 和 Student
的类,需要进行归档和解档。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "Student.h"
@interface Person : NSObject<NSCoding>
//设置属性 @property (nonatomic,strong)UIImage * avatar; @property (nonatomic,copy)NSString * name; @property (nonatomic,assign)NSInteger age; // 嵌套模型 @property (nonatomic,strong)Student * student; @end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #import "Person.h"
@implementation Person
//实现NSCoding协议方法 //解档 - (id)initWithCoder:(NSCoder *)aDecoder{ if (self = [super init]) { self.avatar = [aDecoder decodeObjectForKey:@"avatar"]; self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntegerForKey:@"age"]; // 嵌套模型 self.student = [aDecoder decodeObjectForKey:@"student"]; } return self; } // 归档 - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:self.avatar forKey:@"avatar"]; [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.age forKey:@"age"]; // 嵌套模型 [aCoder encodeObject:self.student forKey:@"student"]; } @end
|
1 2 3
| @interface Student : NSObject<NSCopying> @property (nonatomic,copy)NSString * name; @end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @implementation Student
//实现NSCoding协议方法 //解档 - (id)initWithCoder:(NSCoder *)aDecoder{ if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; } return self; } //归档 - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:self.name forKey:@"name"]; } @end
|
- 接下来只要使用
解/归
档辅助类就可以TestModel类进行解归档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| // 把对象归档是调用NSKeyedArchiver的工厂方法archiverRootObject:toFile方法 NSString * file = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString * path = [file stringByAppendingPathComponent:@"person.data"]; Person * person = [[Person alloc] init]; person.avatar = [UIImage imageNamed:@"123"]; person.name = @"张建"; person.age = 26; // 嵌套模型 Student * student = [[Student alloc] init]; student.name = @"小J"; person.student = student; [NSKeyedArchiver archiveRootObject:person toFile:path]; // 从文件中解档对象就是调用NSKeyedUnarchiver的一个工厂方法unarchiverObjectWithFile:即可 Person * person2 = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; if (person2) { UIImage * avatarImg = person2.avatar; NSString * name = person2.name; NSInteger age = person2.age; Student * student2 = person2.student; NSString * s_name = student2.name; NSLog(@"avatar:%@ name:%@ age:%ld s_name:%@",avatarImg,name,age,s_name); }
|