IOS储存的五个方案
1. NSUserDefaults (偏好设置文件)
2. plist文件
3. 归档 (反归档)
4. SQLite3(FMDB)
5. CoreData
沙盒中相关路径 介绍
- AppName.app 应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以不能在运行时对这个目录中的内容进行修改,否则会导致应用程序无法启动。
- Documents/ 保存应用程序的重要数据文件和用户数据文件等。用户数据基本上都放在这个位置(例如从网上下载的图片或音乐文件),该文件夹在应用程序更新时会自动备份,在连接iTunes时也可以自动同步备份其中的数据。
- Library:这个目录下有两个子目录,可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份.
- Library/Caches: 保存应用程序使用时产生的支持文件和缓存文件(保存应用程序再次启动过程中需要的信息),还有日志文件最好也放在这个目录。iTunes 同步时不会备份该目录并且可能被其他工具清理掉其中的数据。
- Library/Preferences: 保存应用程序的偏好设置文件。NSUserDefaults类创建的数据和plist文件都放在这里。会被iTunes备份。
- tmp/: 保存应用运行时所需要的临时数据。不会被iTunes备份。iPhone重启时,会被清空。
1. NSUserDefaults
NSUserDefaults 介绍
- 在运行时,您使用对象从用户的默认数据库中读取应用程序使用的默认值。缓存信息,以避免每次需要默认值时都必须打开用户的默认数据库。设置默认值时,它将在您的流程中同步更改,并异步更改为持久性存储和其他流程。
- NSUserDefaults 默认对象必须是一个属性列表,也就是说,的一个实例(或对集合的实例的组合)NSData,NSString,NSNumber,NSDate,NSArray,或NSDictionary。如果要存储任何其他类型的对象,通常应将其归档以创建NSData的实例。
- NSUserDefaults用来存储 用户设置 系统配置等一些小的数据。因为数据是明文存储在 plist 文件中,不安全,即使只是修改一个 key 都会 load 整个文件,数据多加载慢( 内存),不适合存储大量数据。
- 它是单例,也是线程安全的,是以键值对 key-value 的形式保存在沙盒中
- 沙盒路径为 Library/Preferences。文件格式为 .plist
- NSUserDefaults返回的值是不可改变的,即使存储的时候是可变的值。对相同的Key赋值约等于一次覆盖。
//NSUserDefaults
//setObject中的key和value可以为除了nil外的任何对象
//setValue中的key只能为字符串 value可以为nil也可以为空对象[NSNull null]以及全部对象
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:@"小明" forKey:@"name"];
//app意外退出或者中断,数据不会被系统写入所以命令synchronize直接同步到文件里,来避免数据的丢失。
[userDefault synchronize];
//取出对应的Key也就是name
[userDefault objectForKey:@"name"];
NSLog(@"name = %@",[userDefault objectForKey:@"name"]);
//打印 name = 小明
2. plist文件
plist文件 介绍
- plist的文件名不能单独命名做"info"、"Info"之类的,是因为与系统属文件重名
- 属性列表是一种XML格式的文件,拓展名为plist
- 对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中
- Plist不能存储自定义对象,成功后会写入到Documents文件中(app)
- xcode中plist文件创建步骤:NewFile —— IOS —— Resource —— Property List
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [cachePath stringByAppendingPathComponent:@"newInfo.plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"小红" forKey:@"name"];
[dict setObject:@"18" forKey:@"age"];
[dict writeToFile:filePath atomically:YES];
NSDictionary *dics = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"age:%@", [dics objectForKey:@"age"]);
3. 归档 (反归档)
归档 (反归档) 介绍
-之前将数据存储到本地,只能是字符串、数组、字典、NSNuber、BOOL等容器类对象,不能将自定义对象进行保存,而通过归档能将所有的对象转化为二进制数据存储到文件中。
- 什么是归档
1.字典,数组,自定义的对象等在存储时需要转换为字节流NSData类型数据,再通过写入文件来进行存储。
- 什么是反归档
2.字节流转换为字典,数组,自定义的类等
- 归档的缺点
3.归档保存数据,只能一次性归档保存以及一次性解压。所以只能针对小量数据,而且对数据操作比较笨拙,即如果想改动数据的某一小部分,还是需要解压整个数据或者归档整个数据。
- 归档的使用场景
4.有些应用没联网时,可以将手机有网时的数据存放在本地,没网时,从本地中取出来这些数据展示。
//Students.h 文件
#import "Students.h"
//遵循NSCoding协议
@interface Students : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
//Students.m 文件
#import "Students.m"
//需要重写两个协议方法
//-(void)encodeWithCoder:(NSCoder *)aCoder方法:
//-(id)initWithCoder:(NSCoder *)aDecoder方法:
// 当将一个自定义对象保存到文件的时候就会调用该方法
// 在该方法中说明如何存储自定义对象的属性
// 也就说在该方法中说清楚存储自定义对象的哪些属性
-(void)encodeWithCoder:(NSCoder *)aCoder
{
NSLog(@"调用了encodeWithCoder:方法");
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
// 当从文件中读取一个对象的时候就会调用该方法
// 在该方法中说明如何读取保存在文件中的对象
// 也就是说在该方法中说清楚怎么读取文件中的对象
-(id)initWithCoder:(NSCoder *)aDecoder
{
NSLog(@"调用了initWithCoder:方法");
//注意:在构造方法中需要先初始化父类的方法
if (self=[super init]) {
self.name=[aDecoder decodeObjectForKey:@"name"];
self.age=[aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
//ViewController.h 文件
#import "ViewController.h"
Students *stu = [[Students alloc]init];
stu.name = @"掰掰";
stu.age = 8;
// 获取文件路径
NSString *docPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [docPath stringByAppendingPathComponent:@"Students.archiver"];
//进行归档
[NSKeyedArchiver archiveRootObject:stu toFile:path];
//反归档
Students *s1 = [NSKeyedUnarchiver unarchiveObjectWithFile:path] ;
NSLog(@"---------%@", s1.name);//掰掰
NSLog(@"---------%ld", (long)s1.age);//8
采用多个对象储存
- (IBAction)archiveManyObject:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject ;
NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //将数据区连接到NSKeyedArchiver对象
Person *p1 = [[Person alloc] init];
p1.name = @"ran1";
p1.age = @"18";
[archiver encodeObject:p1 forKey:@"person1"];
Person *p2 = [[Person alloc] init];
p2.name = @"ran2";
p2.age = @"19";
[archiver encodeObject:p2 forKey:@"person2"];
[archiver finishEncoding];
[data writeToFile:filePath atomically:YES];
}
- (IBAction)unarchiveManyObject:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFilePath = paths.firstObject ;
NSString *filePath = [documentFilePath stringByAppendingPathComponent:@"personModel"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *p1 = [unarchiver decodeObjectForKey:@"person1"];
Person *p2 = [unarchiver decodeObjectForKey:@"person2"];
[unarchiver finishDecoding];
NSLog(@"%@", p1.name);
NSLog(@"%@", p2.name);
}
4. SQLite3
数据库(splite):
splite是一个轻量级,跨平台的小型数据库,可移植性比较高,有着和MySpl几乎相同的数据库语句,以及无需服务器即可使用的优点:
数据库的优点:
存储大量的数据,存储和检索的速度非常快.
能对数据进行大量的聚合,这样比起使用对象来讲操作要快.
数据库的缺点:
它没有提供数据库的创建方式
它的底层是基于C语言框架设计的, 没有面向对象的API, 用起来非常麻烦
发杂的数据模型的数据建表,非常麻烦
在实际开发中我们都是使用的是FMDB第三方开源的数据库,该数据库是基于splite封装的面向对象的框架.
//sqlite3
//1.获取沙盒文件名
NSString *fileName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];
NSLog(@"fileName = %@",fileName);
int result = sqlite3_open(fileName.UTF8String, &_db); //创建(打开)数据库,如果数据库不存在,会自动创建 数据库文件的路径必须以C字符串(而非NSString)传入
if (result == SQLITE_OK) {
NSLog(@"成功打开数据库");
char *errorMesg = NULL;
const char *sql = "create table if not exists t_person (id integer primary key autoincrement, name text, age integer);";
int result = sqlite3_exec(_db, sql, NULL, NULL, &errorMesg); //sqlite3_exec()可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据
if (result == SQLITE_OK) {
NSLog(@"成功创建t_person表");
} else {
NSLog(@"创建t_person表失败:%s",errorMesg);
}
} else {
NSLog(@"打开数据库失败");
}
//sqlite3 添加数据库
- (IBAction)AddSqlite3:(UIButton *)sender {
for (int i = 0; i < 10; i++) {
NSString *name = [NSString stringWithFormat:@"小明-%d",arc4random()%100];
int age = arc4random() % 100;
char *errorMesg = NULL;
NSString *sql = [NSString stringWithFormat:@"insert into t_person (name,age) values ('%@',%d);",name, age];
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"添加数据成功");
} else {
NSLog(@"添加数据失败");
}
}
}
//删除数据库
- (IBAction)sqlite3delete:(id)sender
{
char *errorMesg = NULL;
NSString *sql = @"delete from t_person where age >= 0";
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"删除成功");
}else {
NSLog(@"删除失败");
}
}
//查询数据库
- (IBAction)query:(id)sender
{
const char *sql = "select id, name, age from t_person;"; //"select id, name, age from t_person where age >= 50;"
sqlite3_stmt *stmt = NULL; //定义一个stmt存放结果集
int result = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL); //检测SQL语句的合法性
if (result == SQLITE_OK) {
NSLog(@"查询语句合法");
while (sqlite3_step(stmt) == SQLITE_ROW) {
int ID = sqlite3_column_int(stmt, 0);
const unsigned char *sname = sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:(const char *)sname];
int age = sqlite3_column_int(stmt, 2);
NSLog(@"%d %@ %d",ID, name, age);
}
} else {
NSLog(@"查询语句非法");
}
}
//修改数据库
- (IBAction)sqliteUpdate:(id)sender {
NSString *sql = @"update t_person set name = '哈哈' where age > 60";
char *errorMesg = NULL;
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);
if (result == SQLITE_OK) {
NSLog(@"更改成功");
}else {
NSLog(@"更改失败");
}
}
5. CoreData
CoreData核心类与结构
NSManagedObjectContext(数据上下文)
对象管理上下文,负责数据的实际操作(重要)
作用:插入数据,查询数据,删除数据,更新数据
NSPersistentStoreCoordinator(持久化存储助理)
相当于数据库的连接器
作用:设置数据存储的名字,位置,存储方式,和存储时机
NSManagedObjectModel(数据模型)
数据库所有表格或数据结构,包含各实体的定义信息
作用:添加实体的属性,建立属性之间的关系
操作方法:视图编辑器,或代码
NSManagedObject(被管理的数据记录)
数据库中的表格记录
NSEntityDescription(实体结构)
相当于表格结构
NSFetchRequest(数据请求)
相当于查询语句
后缀为.xcdatamodeld的包
里面是.xcdatamodel文件,用数据模型编辑器编辑
编译后为.momd或.mom文件
参考博客链接