说到数据存储,我们不得不先了解下苹果的沙盒 、如何获取沙盒路径和沙盒目录下对应的文件:
一、沙盒(sandbox)
每一个App都有一个存储空间。iOS系统为每个应用程序创建自己的目录,每个应用程序只能访问自己的目录,不能相互通信。
沙盒主要包括下面几个文件:用模拟器运行 NSLog(@"%@",NSHomeDirectory()); 打印路径;
进入该路径下回看到四个文件 Documents , Libraby , SystemData , tmp四个文件
各个文件的用途 :
1、Documents
获取路径
NSString *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
保存持久化数据,会备份。一般用来存储需要持久化的数据。
一般我们在项目中,我们会吧一些用户的登录信息进行存储,以及搜索历史记录等等一些关键数据。
2、Library 下面有两个文件 Caches 和 Preferences
Caches: iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。
获取路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
Preferences: iTunes同步该应用时会同步此文件夹中的内容,通常保存应用的设置信息。NSUserDefaults存放
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
3、tmp
iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。
获取路径
NSString *path = NSTemporaryDirectory();
二、 下面看下数据存储 包含: Preference 、文件存储、归档、数据库 、 CoreData
1、Preference(偏好设置): NSUserDefaults
可以存储 字典 数组 字符等系统自带的数据类型,自定义的对象无法存储,默认存放字 Library/Preferences 下 具体代码实现
- (void)setDefault {
//可以存储 字典 数组 字符等系统自带的数据类型,自定义的对象无法存储
NSUserDefaults * def = [NSUserDefaults standardUserDefaults];
[def setObject:@"aaaaaa" forKey:@"DEFAULT"];
[def synchronize];
NSLog(@"default ---- %@",[def objectForKey:@"DEFAULT"]);
}
- (void)delDefault {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"DEFAULT"];
NSLog(@"清空了default ---- %@",[[NSUserDefaults standardUserDefaults] objectForKey:@"DEFAULT"]);
}
2 、文件存储
存储系统自带的数据类型,一般实际开发中存储字典、数组,自定义的模型无法进行存储
代码实习
#define LvPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"lvData.plist"]
#define LPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"lData.plist"]
#define LhPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"lhData.plist"]
- (void)createFile{
//文件
NSDictionary *dic = @{@"A":@"123"};
NSArray * arr = @[@"Q",@"123"];
NSString * string = @"aaaaaaaa";
[dic writeToFile:LvPath atomically:YES];
[arr writeToFile:LPath atomically:YES];
[string writeToFile:LhPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
UIButton * fileBtn = [UIButton buttonWithType:UIButtonTypeCustom];
fileBtn.frame = CGRectMake(10, 180, 80, 80);
[fileBtn setTitle:@"file" forState:UIControlStateNormal];
fileBtn.backgroundColor = [UIColor redColor];
[fileBtn addTarget:self action:@selector(setFile) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:fileBtn];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)setFile {
NSArray * arr = [NSArray arrayWithContentsOfFile:LPath];
NSDictionary * dic = [NSDictionary dictionaryWithContentsOfFile:LvPath];
NSString * string = [NSString stringWithContentsOfFile:LhPath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"file :arr--%@ dict---%@ string--%@",arr,dic,string);
}
3、归档(又名序列化)
归档是把对象转为字节码,以文件的形式存储到磁盘上,程序运行过程中或者再次重新打开程序的时候,可以通过解归档(返序列化)还原这些对象。
归档的对象是Foundation框架中的对象
归档和解归档其中任意对象都需要归档和解归档整个文件
归档后的文件是加密的,所以归档文件的扩展名可以随意取
在带键的归档中,每个归档都有一个key值,解归档时key值要与归档时key值匹配
代码实习 下面这个方法是卸载app ,再次装值也是还是之前的
LHKeyChain.m 文件
#import "LHKeyChain.h"
@implementation LHKeyChain
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
+ (void)save:(NSString *)service data:(id)data {
//Get search dictionary
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Delete old item before add new item
SecItemDelete((CFDictionaryRef)keychainQuery);
//Add new object to search dictionary(Attention:the data format)
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
//Add item to keychain with the search dictionary
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}
+ (id)load:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
//Configure the search setting
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
+ (void)deleteKeyData:(NSString *)service {
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
LHKeyChain.h
#import
@interface LHKeyChain : NSObject
+ (void)save:(NSString *)service data:(id)data;
+ (id)load:(NSString *)service;
+ (void)deleteKeyData:(NSString *)service;
@end
调用 //归档
- (void)setArchiver {
NSString *IDFV = [LHKeyChain load:@"IDFV"];
if ([IDFV isEqualToString:@""] || !IDFV) {
IDFV = [UIDevice currentDevice].identifierForVendor.UUIDString;
[LHKeyChain save:@"IDFV" data:IDFV];
}
NSLog(@"archiver---%@",[LHKeyChain load:@"IDFV"]);
[LHKeyChain deleteKeyData:@"IDFV"];
NSLog(@"archiver---%@",[LHKeyChain load:@"IDFV"]);
}
4 、数据库
SQLite数据库的几个特点:
1、基于C语言开发的轻型数据库
2、在iOS中需要使用C语言语法进行数据库操作、访问(无法使用ObjC直接访问,因为libqlite3框架基于C语言编写)
3、SQLite中采用的是动态数据类型,即使创建时定义了一种类型,在实际操作时也可以存储其他类型,但是推荐建库时使用合适的类型
4、建立连接后通常不需要关闭连接
SQLite 使用:
1、在iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件。如图
2、导入头文件,可以使用库中的函数
#import
3、正好现在趁着数据持久化,讲下数据库增、删、改、查;有一个很好的第三方库 FMDB,这里不具体讲,详细使用可以点击了解详情 demo
1)创建并打开数据库
使用 sqlite3_open(<#const char *filename#>, <#sqlite3 **ppDb#>)函数的一些说明:把一个文件名称传递给他,它会自动检测这个文件是否存在,如果不存在的话,会自动创建相应的文件;
代码实现:
//数据库
- (void)createSqlite {
//db是数据库的缩写
sqlite3 * db;
//这里面定义一个数据库存放路径,并获取到
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) lastObject];
NSString * dbPath = [path stringByAppendingString:@"demo.sqlite"];
//因为sqlite是c语言 所以下面需要将OC字符串转换为c语言的字符串
const char * cdbPath = dbPath.UTF8String;
//下面打开数据库 (如果数据库存在的话,会直接打开,反之数据库不存在的话,会自动创建数据库文文件)
int result = sqlite3_open(cdbPath, &db);
if (result == SQLITE_OK) {
NSLog(@"成功打开数据库");
} else {
NSLog(@"数据库打开失败");
}
//数据库 现在有了,接下来创建表
}
运行 查看结果 :
沙盒中已经存在 如图
数据库有了 ,下面就该创建表了
2)语句 sqlite3_exec(<#sqlite3 *#>, <#const char *sql#>, <#int (*callback)(void *, int, char **, char **)#>, <#void *#>, <#char **errmsg#>)
代码实现
//db是数据库的缩写
sqlite3 * db;
//这里面定义一个数据库存放路径,并获取到
NSString *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString * dbPath = [document stringByAppendingPathComponent:@"demo.sqlite"];
//因为sqlite是c语言 所以下面需要将OC字符串转换为c语言的字符串
const char * cdbPath = dbPath.UTF8String;
//下面打开数据库 (如果数据库存在的话,会直接打开,反之数据库不存在的话,会自动创建数据库文文件)
int result = sqlite3_open(cdbPath, &db);
if (result == SQLITE_OK) {
NSLog(@"成功打开数据库");
//数据库 现在有了,接下来创建表
const char * sql = "create table if not exists t_demo (id integer PRIMARY KEY AUTOINCREMENT,title text not null, content text not null)";
char * errorMsg = NULL;
result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);
if (result == SQLITE_OK) {
NSLog(@"表创建成功");
} else {
NSLog(@"表创建失败 %s",errorMsg);
}
} else {
NSLog(@"数据库打开失败");
}
现在表也有了 ,该对表进行操作了
3)插入数据
代码实现
//1.拼接SQL语句
NSString * title = [NSString stringWithFormat:@"title"];
NSString * contet = [NSString stringWithFormat:@"content"];
NSString *sql=[NSString stringWithFormat:@"INSERT INTO t_demo (title,content) VALUES ('%@','%@');",title,contet];
//2.执行SQL语句
char *errmsg=NULL;
sqlite3_exec(db, sql.UTF8String, NULL, NULL, &errmsg);
if (errmsg) {//如果有错误信息
NSLog(@"插入数据失败--%s",errmsg);
}else
{
NSLog(@"插入数据成功----%@",title);
}
代码实现
//1.拼接SQL语句
NSString * title = [NSString stringWithFormat:@"title--12"];
NSString * contet = [NSString stringWithFormat:@"content--13"];
NSString *sql=[NSString stringWithFormat:@"UPDATE t_demo set title = '%@',content = '%@' where id = 2;",title,contet];
//2.执行SQL语句
char *errmsg=NULL;
sqlite3_exec(db, sql.UTF8String, NULL, NULL, &errmsg);
if (errmsg) {//如果有错误信息
NSLog(@"更新数据失败--%s",errmsg);
}else
{
NSLog(@"更新数据成功----%@",title);
}
执行结果
5) 查询数据
语句:sqlite3_prepare_v2(<#sqlite3 *db#>, <#const char *zSql#>, <#int nByte#>, <#sqlite3_stmt **ppStmt#>, <#const char **pzTail#>)
代码实现
const char *sql="SELECT id,title,content FROM t_demo;";
sqlite3_stmt *stmt=NULL;
//进行查询前的准备工作
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK) {//SQL语句没有问题
NSLog(@"查询语句没有问题");
//每调用一次sqlite3_step函数,stmt就会指向下一条记录
while (sqlite3_step(stmt)==SQLITE_ROW) {//找到一条记录
//取出数据
//(1)取出第0列字段的值(int类型的值)
int ID = sqlite3_column_int(stmt, 0);
//(2)取出第1列字段的值(text类型的值)
const unsigned char * title = sqlite3_column_text(stmt, 1);
//(3)取出第2列字段的值(int类型的值)
const unsigned char * content = sqlite3_column_text(stmt, 2);
// NSLog(@"%d %s %d",ID,name,age);
printf("%d %s %s\n",ID,title,content);
}
}else
{
NSLog(@"查询语句有问题");
}
//1.拼接SQL语句
NSString *sql=[NSString stringWithFormat:@"DELETE from t_demo where id = 2;"];
//2.执行SQL语句
char *errmsg=NULL;
sqlite3_exec(db, sql.UTF8String, NULL, NULL, &errmsg);
if (errmsg) {//如果有错误信息
NSLog(@"删除数据失败--%s",errmsg);
}else
{
NSLog(@"删除数据成功");
}
5、CoreData
Core Data是iOS5之后才出现的一个框架,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也可以使用其他方式,比如:数据库文件,XML,二进制文件,内存等。CoreData 提供了 对象-关系映射(ORM) 功能,能够将保存在数据库中的数据还原成OC对象。在此数据操作期间,我们不需要编写任何SQL语句;
本文 只讲简单的使用 下篇文字将学习CoreData具体的知识
代码实现 :
LHModel 类:
LHModel.h
#import
@interface LHModel : NSManagedObject
@property (nonatomic, copy) NSString * title;
@property (nonatomic, copy) NSString * content;
@end
LJModel.m
#import "LHModel.h"
@implementation LHModel
@synthesize title;
@synthesize content;
@end
CoreDataViewController类 这个是我自己定义的类
CoreDataViewController.m
#import "CoreDataViewController.h"
#import
#import "LHModel.h"
@interface CoreDataViewController ()
/**
* 上下文 容器
* 存放的是 所有从数据库中取出的转换成OC对象
*/
@property (strong, nonatomic) NSManagedObjectContext * managedObjectContext;
/* 读取解析 .momd文件中的内容 */
@property (strong, nonatomic) NSManagedObjectModel * managedObjectModel;
/* 连接的类,处理数据库数据和OC数据底层的相互转换 */
@property (strong, nonatomic) NSPersistentStoreCoordinator * persistentStoreCoordinator;
@end
@implementation CoreDataViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
NSLog(@"%@",self.managedObjectContext);
//插入一条数据 (往LHModel表中插入一条数据)
//NSEntityDescription 实体类
//EntityForName 实体名称(表名)
LHModel * model = [NSEntityDescription insertNewObjectForEntityForName:@"LHModel1" inManagedObjectContext:self.managedObjectContext];
//赋值
model.title = @"缓存1";
model.content = @"缓存1内容";
//同步操作 把context中的数据同步到数据库中
[self saveContext];
// 查询数据
// 创建查询请求
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"LHModel1"];
// Context 执行请求(执行查询操作) 数组中存放的是oc类的对象(People类的对象)
NSArray * array = [self.managedObjectContext executeFetchRequest:request error:nil];
for (LHModel *lhModel in array)
{
NSLog(@"%@",lhModel.title);
}
//查询特定条件数据
NSFetchRequest * request1 = [NSFetchRequest fetchRequestWithEntityName:@"LHModel1"];
//使用谓词指定查询的判定条件
NSString * title = @"缓存1";
// NSString *predStr = [NSString stringWithFormat:@"%@ AND (%@ CONTAINS \"%@\")", kPredicateStr_MovieItem_MoviesInCatalog, titleForSearch, title];
NSPredicate * predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"SELF.title == '%@'",title]];
//关联判定条件
[request1 setPredicate:predicate];
//执行查询操作
NSArray * array2 = [self.managedObjectContext executeFetchRequest:request1 error:nil];
for (LHModel * lhModel in array2)
{
NSLog(@"%@",lhModel.title);
}
//更改数据
//获取出要修改的数据
LHModel * lhModel = [array lastObject];
//修改属性
lhModel.title = @"缓存2";
lhModel.content = @"缓存2内容";
//同步数据
[self saveContext];
//删除数据
LHModel * lhModel1 = [array lastObject];
[self.managedObjectContext deleteObject:lhModel1];
//同步数据
[self saveContext];
}
//managedObjectModel 属性的getter方法
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) return _managedObjectModel;
//.xcdatamodeld文件 编译之后变成.momd文件 (.mom文件)
NSURL * modelURL = [[NSBundle mainBundle] URLForResource:@"MemoryData" withExtension:@"momd"];
//把文件的内容读取到managedObjectModel中
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
//Coordinator 调度者负责数据库的操作 创建数据库 打开数据 增删改查数据
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
// 设置数据库存放的路径
NSURL * storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"MemoryData.sqlite"];
//根据model创建了persistentStoreCoordinator
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSError * error = nil;
//如果没有得到数据库
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(@"错误信息: %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
//容器类 存放OC的对象
-(NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) return _managedObjectContext;
NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];
if (!coordinator)
{
return nil;
}
/* 创建context对象 NSManagedObjectContext 实例提供一个线程。我们需要将这个 init 方法替换成 -initWithConcurrency: 方法。这个方法配置了 NSManagedObjectContext 实例化所在的线程。
这就意味着我们要确定在哪个线程上实例化我们的 NSManagedObjectContext ,主线程,还是另外创建一个后台线程。我们可以选择的参数有:
NSPrivateQueueConcurrencyType
NSMainQueueConcurrencyType
在这里,我把它配置成在主线程上进行实例化(一般选择主线程就可以)*/
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
//让context和coordinator关联 context可以对数据进行增删改查功能
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
#pragma mark - Core Data Saving support
-(void)saveContext
{
NSManagedObjectContext * managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
NSError * error = nil;
// hasChanges 判断数据是否更改
// sava 同步数据库中的数据
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])
{
NSLog(@"错误信息: %@, %@", error, [error userInfo]);
}
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
运行结果 :
希望大家一起学习: 附有demo
csdn demo下载地址
https://download.csdn.net/download/u013983033/10745909
github demo 下载地址
https://github.com/lvhome/DataMemory