数据持久化
在项目开发过程中,我们通常会根据需求存储一些信息,所以进行数据持久化是我们开发者的必备技能包。
1、沙盒
APP默认情况下只能访问程序自己的目录,这个目录就被称之为沙盒,NSHomeDirectory()
可获取沙盒路径。
- Documents:一般用来保存需要持久化的数据,比如登录信息、搜索记录等等。
- Library:它包含两个文件夹Caches和Preferences,Caches 保存缓存的数据,内存不足时会被清除,而Preferences 保存持久化的数据,NSUserDefaults默认存放在此文件夹。
- SystemData:新加入的文件夹,具体存储规则 不知。
- tmp:保存一些临时数据,应该退出时会清除该数据。
2、数据持久化的几种方式
2.1、概念
属性列表:plist文件存储的数据只能用NSArray或是NSDictionary读取。
偏好设置:NSUserDefaults 存储的数据类型有 NSData,NSString,NSNumber,NSDate, NSArray,NSDictionary,如果要存储图片的话,需要先将图片转成NSData类型,它的缺点是无法存储自定义数据类型。
归档:轻量级数据存储,数据经过归档处理会转化成二进制数据,安全性要比属性列表要高,数据归档要遵守NSCoding协议。
SQLite:轻量级关系数据库,结构化查询语言,不区分大小写 能够进行建表、增、删、改、查等等,一般步骤是新建一张表 –> 添加多个字段 –> 添加多行记录,可用第三方框架 FMDB
CoreData:iOS5之后出现的一个框架(不是数据库),它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中。
2.2、属性列表 plist文件
//1.获取沙盒路径
NSString *homeString = NSHomeDirectory();
//2.拼接Documents路径
NSString *documentsString = [homeString stringByAppendingPathComponent:@"Documents"];
//3.建立name.plist文件
NSString *filePath = [documentsString stringByAppendingPathComponent:@"name.plist"];
存取数据
NSArray *data = @[@"1", @"2", @"5"];
[data writeToFile:filePath atomically:YES];
读取数据
NSArray *readData = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@", readData);
删除数据
NSFileManager *manager = [NSFileManager defaultManager];
BOOL isSuccess = [manager removeItemAtPath:filePath error:nil];
if (isSuccess) {
NSLog(@"删除成功");
}else {
NSLog(@"删除失败");
}
2.3、偏好设置 NSUserDefaults
//模拟数据
NSString *userName = @"王老师";
NSString *key = @"USERNAME";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//存储数据
[userDefaults setObject:userName forKey:key];
//读取数据
[userDefaults objectForKey:key];
//删除数据
[userDefaults removeObjectForKey:key];
2.4、归档 NSKeyedArchiver
//获取文件路径 -- 获取Documents路径 除了拼接也可以直接获取
NSString *plistPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
plistPath = [plistPath stringByAppendingPathComponent:@"test.plist"];
保存数据 – 归档
NSString *testString = @"123";
NSMutableArray *array = [NSMutableArray array];
NSData *archiverData = [NSKeyedArchiver archivedDataWithRootObject:testString];
[array addObject:archiverData];
[array writeToFile:plistPath atomically:YES];
读取数据 – 解档
NSArray *unarchiveArrray = [NSArray arrayWithContentsOfFile:plistPath];
NSData *unarchiveData = unarchiveArrray.firstObject;
NSString *unarchiveString = [NSKeyedUnarchiver unarchiveObjectWithData:unarchiveData];
NSLog(@"%@", unarchiveString);
删除数据
NSFileManager *archiveManager = [NSFileManager defaultManager];
[archiveManager removeItemAtPath:plistPath error:nil];
注:自定义数据要遵守NSCoding协议
2.5、 SQLite
创建路径
NSString *sqliteFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"data.sqlite"];
sqlite3 *db;
打开数据库,创建表
if (sqlite3_open(sqliteFilePath.UTF8String, &db) != SQLITE_OK) {
//打开失败
NSLog(@"打开失败");
}else {
//创建表
}
创建表(例子用户表:字段 name-名字、age-年龄、icon-头像
/*
1、创建表的SQL语句 create table
2、SQLite3支持 NULL、INTEGER(整型)、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型
3、if not exists表示:如果不存在数据表,则新建一个, 若存在,则此命令自动退出.
*/
// **** 创建一个用户表 User、 字段 name-名字、age-年龄、icon-头像
NSString *creatUserTable = @"create table if not exists 'User' ( 'id' integer not null primary key autoincrement,'name' text,'age' integer,'icon' text);";
//项目中一般不会只有一个表
NSArray *SQL_ARR = [NSArray arrayWithObjects:creatUserTable, nil];
for (NSString *sqlTable in SQL_ARR) {
//执行SQL语句
char *error;
if (sqlite3_exec(db, sqlTable.UTF8String, nil, nil, &error) == SQLITE_OK) {
NSLog(@"执行成功");
}else {
NSLog(@"SQLiteManager执行SQL语句出错:%s",error);
}
}
插入数据 insert into
NSString *insertSQL = [NSString stringWithFormat:@"insert into 'User' (name,age,icon) values ('%@',%f,'%@');", @"鲁尼", 9.0, @"www.baidu.com"];
//执行SQL语句
char *error;
if (sqlite3_exec(db, insertSQL.UTF8String, nil, nil, &error) == SQLITE_OK) {
NSLog(@"插入成功");
}else {
NSLog(@"SQLiteManager执行SQL语句出错:%s",error);
}
查询数据 select
//查询表中所有的数据 distinct(去除重复的)
NSString *selectAllSql = @"select name,age,icon from 'User'";
//查询表中年龄小于10的
selectAllSql = @"select * from User where age < 10";
//执行SQL语句
/*
1、 参数一:数据库对象
2、 参数二:查询语句
3、 参数三:查询语句的长度:-1
4、 参数四:句柄(游标对象)
*/
sqlite3_stmt *stmt = nil;
if (sqlite3_prepare_v2(db, selectAllSql.UTF8String, -1, &stmt, nil) != SQLITE_OK) {
NSLog(@"准备查询失败!");
}else {
//开始查询数据
//定义一个存放数据字典的可变数组
NSMutableArray *dictArrM = [[NSMutableArray alloc] init];
while (sqlite3_step(stmt) == SQLITE_ROW) {
//一共获取表中所有列数(字段数)
int columnCount = sqlite3_column_count(stmt);
//定义存放字段数据的字典
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (int i = 0; i < columnCount; i++) {
// 取出i位置列的字段名,作为字典的键key
const char *cKey = sqlite3_column_name(stmt, i);
NSString *key = [NSString stringWithUTF8String:cKey];
//取出i位置存储的值,作为字典的值value
const char *cValue = (const char *)sqlite3_column_text(stmt, i);
NSString *value = [NSString stringWithUTF8String:cValue];
//将此行数据 中此字段中key和value包装成 字典
[dict setObject:value forKey:key];
}
[dictArrM addObject:dict];
}
NSLog(@"%@", dictArrM);
}
更新数据 update
NSString *updateSql = [NSString stringWithFormat:@"update 'User' set age='%d' where icon = '%@'", 8, @"www.baidu.com"];
//执行SQL语句
char *updateError;
if (sqlite3_exec(db, updateSql.UTF8String, nil, nil, &updateError) == SQLITE_OK) {
NSLog(@"更新成功");
}else {
NSLog(@"SQLiteManager执行SQL语句出错:%s",updateError);
}
删除数据 delete
//删除数据库所有数据 delete
NSString *deleteSql = @"delete from 'User'";
//删除年龄为9的数据
deleteSql = [NSString stringWithFormat:@"delete from 'User' where age='%d'", 8];
//执行SQL语句
char *deleteError;
if (sqlite3_exec(db, deleteSql.UTF8String, nil, nil, &deleteError) == SQLITE_OK) {
NSLog(@"删除成功");
}else {
NSLog(@"SQLiteManager执行SQL语句出错:%s",deleteError);
}
删除表 drop
NSString *dropSql = @"drop table if exists 'User'";
//执行SQL语句
char *dropError;
if (sqlite3_exec(db, dropSql.UTF8String, nil, nil, &dropError) == SQLITE_OK) {
NSLog(@"删除表成功");
}else {
NSLog(@"SQLiteManager执行SQL语句出错:%s",dropError);
}
操作完成后,要关闭数据库
//关闭数据库
sqlite3_close(db);
2.6、 CoreData
建项目时 选择Use Core Data,生成 . xcdatamodeld 文件
在 . xcdatamodeld 文件中添加模型和属性,例如 User模型,属性name(姓名)和age(年龄)
一般我们会选择手动生成继承自NSManagedObject的User类,而.xcdatamodeld文件 在编译时会自动生成User类,所以我们可以选择禁用模型的自动生成功能,在Class中的Codegen改成Manul/None。
选择Create NSmanagedObject Subclass, 手动生成类文件
获取程序委托中唯一托管的对象,用于 存储,读取,删除数据
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = delegate.persistentContainer.viewContext;
创建模型对象 –> 把数据存储在实体对象 –> 保存同步文件
User *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:context];
user.name = @"欧阳哒哒";
user.age = 24;
BOOL isSave = [context save:&error];
if (isSave) {
NSLog(@"保存成功");
}
初始化查询类 –> 配置查询条件 –> 执行查询
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"User"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"age > 10"];
NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
NSLog(@"%@", results);
if (results.count > 0) {
User *userExample = results.firstObject;
NSLog(@"姓名 %@", userExample.name);
}
删除,将查询的结果删除,并保存
for (id object in results) {
[context deleteObject:object];
}
BOOL isdelete = [context save:&error];
if (isdelete) {
NSLog(@"删除成功");
}
2.7、本篇 Demo 在此,可点击下载。