iOS 之 FMDB、CoreData、Plist、NSUserDefault

简述

数据储存可以分为数据结构和储存方式。数据结构就是数据存在的的形式。例如 NSDictionnary、NSArray、NSSet等这些简单的对象,也有像CoreData那样的关系模型。储存方式在机器内则分为两种:1、内存;2、闪存。内存存储是临时的,但是运行速率非常快,闪存则是一种持久化存储,效率非常低。我们常说的归档就是将内存中的数据转移到闪存就行持久化保存。我们只有把内存和闪存结合起来进行操作才算完整的数据储存方案。盗图来说明:

1.png

常见方式

iOS中常用的有:SQLite、CoreData、Plist、NSUserDefault。

  1. NSUserDefault: 用于储存配置信息
  2. SQLite: 主要用来储存数量较多的小型数据
  3. CoreData: 类似SQLite,但没SQLite那么灵活
  4. Plist: 用于存储图像、音频等大体积数据

NSUserDefault

  1. 常规储存
    NSUserDefault可以看做为APP的一个全局单例,整个APP中只有一个实例对象,可以用于永久保存数据,非常容易操作。它支持储存的数据类型有:NSNumber、NSString、NSDictionary、NSArray、NSDate、BOOL。他的数据存储是通过key--value一一对应的(key必须唯一);

  2. 存储自定义对象
    储存小数量的自定义数据可以用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之间的通信限制。

目录
  1. Documents
    Documents主要用来保存APP运行时需要持久化保存的数据,该目录会被iTunes同步时备份

  2. Library
    2.1 . Caches
    Caches是用来保存APP运行时需要持久化储存的数据,但不会被iTunes同步,所以该目录适合保存一些体积大而且不需要备份的数据

    2.2. Prefrernces
    PreFrernces是用来保存应用偏好设置的,iOS的设置应用会在该目录中查找应用设置的信息,也会被iTunes同步到备份

  3. 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:

  1. FMDataBase:它主要用来执行SQL语句,但是它线程不安全的
  2. FMResult 用于查询结果
  3. 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语句
  1. 创建表语句
    create table if not exists StudentTab(numberID integer primary key not NULL ,name text,headImage blob,scroll real)

这表示如果不存在StudentTab,则创建一个NumberID自动增加的的序号值,name是文本字符串类型,head为二进制类型,scroll为浮点型类型

  1. 插入语句
insert into Student(name,headImage,scroll)  values (?,?,?)
insert into Student values (:name,:headImage,:scorll);
insert into Student(name,headImage,scroll) values('%@','%@','%lf') 
//这三句等价
  1. 更新语句
update Student set name = ? where scoll = ?
  1. 删除语句
delete  from Student where name = ?
drop table if exists Student
  1. 查询语句
select * from Student where name = ?

特殊用法:
如果你要保存的参数个数未知或者数量较多 我们可以用一下这些方法 给让你感受到很好的体验

 - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
 - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;

数据库的事务也很好用 inDeferredTransaction

CoreData

  1. NSManagedObjectModel(被管理的对象模型)相当于实体,但它也包含了实体间的关系,
  2. NSManagedObjectContext(被管理的对象上下文)操作的作用有:插入数据、查询、更新、删除
  3. NSPersistentStoreCoordinator(持久化储存助理)它充当这与数据库连接的角色
  4. NSPredicate(相当于查询条件)
  5. NSFetchRequest(获取数据的请求)
  6. NSEntityDescription(实体结构)
  7. 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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容