iOS 之 FMDB、CoreData、Plist、NSUse
简述
数据储存可以分为数据结构和储存方式。数据结构就是数据存在的的形式。例如 NSDictionnary、NSArray、NSSet等这些简单的对象,也有像CoreData那样的关系模型。储存方式在机器内则分为两种:1、内存;2、闪存。内存存储是临时的,但是运行速率非常快,闪存则是一种持久化存储,效率非常低。我们常说的归档就是将内存中的数据转移到闪存就行持久化保存。我们只有把内存和闪存结合起来进行操作才算完整的数据储存方案。盗图来说明:
1.png常见方式
iOS中常用的有:SQLite、CoreData、Plist、NSUserDefault。
- NSUserDefault: 用于储存配置信息
- SQLite: 主要用来储存数量较多的小型数据
- CoreData: 类似SQLite,但没SQLite那么灵活
- Plist: 用于存储图像、音频等大体积数据
NSUserDefault
-
常规储存
NSUserDefault可以看做为APP的一个全局单例,整个APP中只有一个实例对象,可以用于永久保存数据,非常容易操作。它支持储存的数据类型有:NSNumber、NSString、NSDictionary、NSArray、NSDate、BOOL。他的数据存储是通过key--value一一对应的(key必须唯一); -
存储自定义对象
储存小数量的自定义数据可以用NSUserDefault,但是这个自定义数据必须实现归档协议,不然会存不进去,只有转化成NSData类型才能通过NSUserDefault储存,如果是多个自定义数据可以添加到数组中,然后归档存储到NSUserDefault中。
归档 解档
//归档
-(void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
const char * propertyCsString = property_getName(properties[i]);
NSString * propertyName = [NSString stringWithCString:propertyCsString encoding:NSUTF8StringEncoding];
id propertyValue = [self valueForKey:propertyName];
[aCoder encodeObject:propertyValue forKey:propertyName];
}
free(properties);
}
//解档
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
const char *property_csNam = property_getName(properties[i]);
NSString *p_key = [NSString stringWithCString:property_csNam encoding:NSUTF8StringEncoding];
id value = [aDecoder decodeObjectForKey:p_key];
[self setValue:value forKey:p_key];
}
free(properties);
}
return self
}
///copy 协议
-(id)copyWithZone:(NSZone *)zone{
StudentModel *s_model = [[self copyWithZone:zone] init];
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
const char *propertyCS = property_getName(properties[i]);
NSString *p_key = [NSString stringWithCString:propertyCS encoding:NSUTF8StringEncoding];
id p_value = [self valueForKey:p_key];
[s_model setValue:p_value forKey:p_key];
}
free(properties);
return s_model;
}
//存储自定义对象
StudentModel *s_model = [[StudentModel alloc] init];
s_model.s_name = @"xiaoqiang";
s_model.s_id = @"22";
s_model.s_password = @"123456";
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:s_model];
[u_default setObject:data forKey:@"s_student"];
[u_default synchronize];
NSData *rachiverData = [u_default objectForKey:@"s_student"];
StudentModel *model = [NSKeyedUnarchiver unarchiveObjectWithData:rachiverData];
NSLog(@"\n name is %@ \n id is %@ \n password is %@",model.s_name,model.s_id,model.s_password);
Plist
沙盒
机制:
iOS沙盒机制就是指该APP只能访问自己的沙盒目录下的文件,不能访问其他APP的沙盒文件,主要用来保存图像、图标、声音、属性列表,文本等,不过iOS 8后新开放了几个固定系统区域的扩展机制extension,它可以一定程度上解决APP之间的通信限制。
目录
-
Documents
Documents主要用来保存APP运行时需要持久化保存的数据,该目录会被iTunes同步时备份 -
Library
2.1 . Caches
Caches是用来保存APP运行时需要持久化储存的数据,但不会被iTunes同步,所以该目录适合保存一些体积大而且不需要备份的数据2.2. Prefrernces
PreFrernces是用来保存应用偏好设置的,iOS的设置应用会在该目录中查找应用设置的信息,也会被iTunes同步到备份 -
tmp
保存APP运行时的临时数据,关闭APP后会被自动删除
获取目录方式
拼接获取:
NSString *homePath = NSHomeDirectory();
NSString *path = [homePath stringByAppendingPathComponent:@"Documents"];
return [path stringByAppendingPathComponent:suffix]; `
打印得到的路径:/var/mobile/Containers/Data/Application/0FB7706E-625C-482C-B1DE-DDD241BB3220/Documents/test.plist
利用NSSearchPathForDirectoriesInDomains函数获得Documents目录:
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = pathArr[0];
return [path stringByAppendingPathComponent:suffix];
打印得到的路径:` /var/mobile/Containers/Data/Application/0FB7706E-625C-482C-B1DE-DDD241BB3220/Documents/test.plist
第一个参数为代表文件路径 一般用到NSDocumentDirectory或者NSCachesDirectory 第二个参数一般为NSUserDomainMask,第三个参数代表是否打开一般为YES,上面PathArr 一般只能或得一个元素,
补充
获取沙盒tmp目录的方法:
NSString *tmp = NSTemporaryDirectory();
获取沙盒caches目录方法:
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
获取应用沙盒preference目录的方法:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
言归正传继续plist操作笔记 plist储存的数据:array,dictionary,string,bool,date,data这几种类型,如果要存储自定义的也要实现归档协议然后转换成NSData类型储存,plist主要用来存储大文件。
NSString *path = [self getPlistLibraryWithSuffix:@"test.plist"];
NSMutableArray *stu_array = [NSMutableArray new];
for (int i = 0; i < 10; i ++) {
StudentModel *s_model = [StudentModel new];
s_model.s_name = [NSString stringWithFormat:@"name%d",i];
s_model.s_id = [NSString stringWithFormat:@"%d",100 + i];
s_model.s_password = @"123456";
[stu_array addObject:s_model];
}
///写入
NSData *archiverData = [NSKeyedArchiver archivedDataWithRootObject:stu_array];
BOOL isSuc = [archiverData writeToFile:path atomically:YES];
// BOOL isSuc = [NSKeyedArchiver archiveRootObject:stu_array toFile:path];
NSLog(@"%d",isSuc);
///读取
NSData *unarchiverData = [NSData dataWithContentsOfFile:path];
NSArray *unArchiverArr = [NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData];
// NSArray *unArchiverArr = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"%@==",unArchiverArr);
SQLite:
iOS的SDK有SQLite的库,我们可以自建SQLite数据库,SQLite每次写入数据都会产生IO消耗,把数据归档到相应的文件。其实SQLite也跟NSUserDefaults差不多,适合储存基础类型的小数据,不过它更适合存储大量的数据,类似聊天记录这样的数据查询和储存。SQLite的操作比较复杂,因为它不是面向对象的,我们用起来很不习惯,但是我们可以用第三发框架FMDB,在工程中pod FMDB 进来。
FMDB:
- FMDataBase:它主要用来执行SQL语句,但是它线程不安全的
- FMResult 用于查询结果
- FMDatabaseQueue 多余多线程查询或者更新 它是线程安全的
FMDB几个主要的方法
FMDB中除了查询之外都可以称作为更新,create、delete,update、insert、drop,使用executeUpdate:方法更新
更新
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
查询
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
SQL语句
- 创建表语句
create table if not exists StudentTab(numberID integer primary key not NULL ,name text,headImage blob,scroll real)
这表示如果不存在StudentTab,则创建一个NumberID自动增加的的序号值,name是文本字符串类型,head为二进制类型,scroll为浮点型类型
- 插入语句
insert into Student(name,headImage,scroll) values (?,?,?)
insert into Student values (:name,:headImage,:scorll);
insert into Student(name,headImage,scroll) values('%@','%@','%lf')
//这三句等价
- 更新语句
update Student set name = ? where scoll = ?
- 删除语句
delete from Student where name = ?
drop table if exists Student
- 查询语句
select * from Student where name = ?
特殊用法:
如果你要保存的参数个数未知或者数量较多 我们可以用一下这些方法 给让你感受到很好的体验
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
数据库的事务也很好用 inDeferredTransaction
CoreData
- NSManagedObjectModel(被管理的对象模型)相当于实体,但它也包含了实体间的关系,
- NSManagedObjectContext(被管理的对象上下文)操作的作用有:插入数据、查询、更新、删除
- NSPersistentStoreCoordinator(持久化储存助理)它充当这与数据库连接的角色
- NSPredicate(相当于查询条件)
- NSFetchRequest(获取数据的请求)
- NSEntityDescription(实体结构)
- NSFetchedResultsController:监听数据变化 实现代理 当数据发生变化会尽代理
NSFetchRequest *result = [NSFetchRequest fetchRequestWithEntityName:@"User"];`
NSSortDescriptor * desciptor = [NSSortDescriptor sortDescriptorWithKey:@"u_name" ascending:YES];
[result setSortDescriptors:@[desciptor]];
NSManagedObjectContext *context = self.manager.mainManagedObjectContext;
self.userFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:result managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
self.userFetchedResultsController.delegate = self;
版本迁移
coredata需要注意版本更新数据迁移处理 一般加自动迁移处理
//自动迁移
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
abort();
}
return _persistentStoreCoordinator;
}
插入
User *user = (User *)[NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.manager.mainManagedObjectContext];
[user setU_age:@"26"];
[user setU_name:@"liu"];
[user setU_sex:@"1"];
NSError *error ;
BOOL isSuc =[self.manager.mainManagedObjectContext save:&error];
删除
NSFetchRequest *result = [[NSFetchRequest alloc] init];
NSEntityDescription *user = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.manager.mainManagedObjectContext];
[result setEntity:user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"u_name = %@",@"liu"];
[result setPredicate:predicate];
NSError *error = nil;
NSArray *arr = [self.manager.mainManagedObjectContext executeFetchRequest:result error:&error];
if (arr.count == 0) {
NSLog(@"error is %@",error);
}else{
for (User *temp in arr) {
[self.manager.mainManagedObjectContext deleteObject:temp];
}
}
[self.manager.mainManagedObjectContext saveAndWait:YES error:&error];
更新:
NSFetchRequest *result = [[NSFetchRequest alloc] init];
NSEntityDescription *user = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.manager.mainManagedObjectContext];
[result setEntity:user];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"u_name = %@",@"liu"];
[result setPredicate:predicate];
NSError *erro = nil;
NSArray *arr = [self.manager.mainManagedObjectContext executeFetchRequest:result error:&erro];
if (arr.count == 0) {
NSLog(@"%@ error ",erro);
}else{
for (User *tempUser in arr) {
tempUser.u_age = @"18";
}
}
[self.manager.mainManagedObjectContext save:&erro];
查询
NSFetchRequest *result = [[NSFetchRequest alloc] init];
NSEntityDescription *user = [NSEntityDescription entityForName:@"User" inManagedObjectContext:self.manager.mainManagedObjectContext];
[result setEntity:user];
NSError *error ;
NSMutableArray *arr = [[self.manager.mainManagedObjectContext executeFetchRequest:result error:&error] mutableCopy];
if (arr.count == 0) {
NSLog(@"error is %@",error);
}
else{
for (User *tempUser in arr) {
NSLog(@"%@==%@===%@",tempUser.u_name,tempUser.u_age,tempUser.u_sex);
}
}
线程安全处理
+(id) instanceChildManagedObjectContext {
NSManagedObjectContext *context = nil;
if ([NSThread isMainThread]) {
context = [[CoreDataManager sharedCoreDataManager] mainManagedObjectContext];
} else {
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = [[CoreDataManager sharedCoreDataManager] mainManagedObjectContext];
}
return context;
}