数据持久化:概述

分类

简单来说,IOS的数据持久化有四种方式:

  1. 属性列表
  2. 对象归档
  3. iOS的嵌入式关系数据库SQLite3
  4. 苹果公司提供的持久化工具Core Data

下面以带有四个TextLabel的界面为例,这个界面的功能是用户在TextLabel中输入值,系统将输入的内容的保存起来,再次启动程序的时候,TextLabel默认显示的仍然是用户之前输入的值。分别用IOS数据持久化的四种方式来实现上述功能。
完成这个例子需要三步——

  1. 保存label中的值就是写文件
  2. label显示默认值就是读文件;
  3. 选择相应的持久化方式,依靠这种方式进行读写。

属性列表


//1.读
-(void)viewDidLoad{

    //通过NSFileManager来检查数据是否存在
    NSString *filePath = [self dataFilePath]
    if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ){
      //文件存在 do something
      //这就把文件读出来了,读的方法是,声明一个与文件内容相同类型的变量,取出即可
      NSArray *array = [[NSArray alloc]initWithContentOfFile: filePath];

      //赋值到需要的地方
      UILabel *lable = self.textLabel;
      label.text = array[0]; 

     /*
     NOTE : 
           self.textLabel为该页面的UI变量,最后的目的是要把咋文件读出来的值显示在这里
           label先把指向self.textLabel的指针取到,然后由label来取值

           ***为何不直接      self.textLabel.text = array[0];       呢??

     */
     }

//下面是与归档编解码无关的部分,监听self,如果发生应用终止运行则及时保存数据
    UIApplication *app = [UIApplication sharedApplication];
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(applicationWillResignActive:)
     name:UIApplicationWillResignActiveNotification
     object:app];


    }

2.写文件
-(void)applicationWillResignActive:(NSNotification *)notification{
    NSString *filePath = [self dataFilePath];
    
    NSArray *array = @[@"a",@"b",@"c",@"d"];//=[self.lineFields valueFoeKey:@"text"];

    [array writeToFile:filePath atomically:YES];

}
//这个函数名字和形式比较特殊,只把它当做一个滴啊有特殊功能的函数就好
//这个方法应是作为observer的一个selector,作用是:使应用在进入后台或者终止运行时保存数据,只要应用不在是前正在与用户交互的应用就会发布通知,例如不小心按了home键或者突然有电话打进来


//获取Document文件路径
-(NSString)dataFilePath{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
    
    NSString *documentDirectory = paths[0];

    return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
    
    //读取tmp目录
    //NSString *tmpPath = NSTemporaryDirectory();
    //NSString *tempFile = [tempPath stringByAppendingPathComponent:@"data.plist"];//体现出用的是“属性列表”的方式
}

对象归档


#import <UIKit/UIKit.h>

//1.读就是解码的过程
-(void)viewDidLoad{

    NSString *filePath = [self dataFilePath]
    if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ){

    //解码通道
    NSData *data = [[NSMutableData alloc] initWithContentOfFile:filePath];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData];
    //根据编码时设定的key找到要解码的对象
    MyObject *myObject = [unarchiver decodeObjectForKey:myObjectKey];
    //
    [unarchiver finishDecoding];
   //READ
    UILabel *label.text = myObject[ObjectAtIndex:somenumber];

    }

    //下面是与归档编解码无关的部分,监听self,如果发生应用终止运行则及时保存数据
    UIApplication *app = [UIApplication sharedApplication];
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(applicationWillResignActive:)
     name:UIApplicationWillResignActiveNotification
     object:app];
    
}


2.写就是编码的过程
-(void)applicationWillResignActive:(NSNotification *)notification{
    //文件写入的地点,要有
    NSString *filePath = [self dataFilePath];

    //要保存的对象
    MyObject *myObject = [[MyObject alloc]init];
    myObject addObject:UILabel.text;//进行一些赋值操作,这里label没有正常声明
    //打开“通道”(不知道这样形容合适不),必须要做的两个步骤
    NSMutableData *data = [[NSMutableData alloc]init];
    NSKeyedArchiver *archiver = [[NSKeyArchiver alloc]initFoeWritingWithMutableData:data];
    //建立键值关系,解码时通过该key去找应该解码的对象
    [archiver encodeObject:myObject forKey:myObjectKey];
    //
    [archiver finishEncoding];
    //WRITE
    [data writeToFile :filePath atomically:YES];
    
}

//获取文件路径
-(NSString)dataFilePath{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
    
    NSString *documentDirectory = paths[0];

    return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
    return [documentDirectory stringByAppendingPathComponent:@"data.archive"]//体现出用的“归档”的方式
    
}

SQLite3


/*
NOTE:
1.SQL语句通过*char 传递而非NSString    两者的相互转换方式为:
 const char *stringPath = [pathString UTF8String];

2.C语言小知识:如果两个内联字符串之间只有空白(或者换行符),而没有自他字符,则两个字符串将会被连接为一个字符串
*/
//基础使用
//1.声明和创建
 sqlite3 *database;
 int result = sqlite3_open("/path/to/database/file",&database);//result = SQLITE_OK if open database successfully

//2.执行语句
 //2-1用于updata insert delete等不反回任何数据的命令 ——  sqlite3_exce
 char *errorMsg;
 const char *createSQL = "CREATE TABLE IF NOT EXISTS PEOPLE" "(ID INTEGER PROMARY KEY AUTOINCREMENT ,FIELD_DATA TEXT)";
 int reslut = sqlite3_exec(database,createSQL,NULL,NULL,&errorMsg);//errorMsg将对错误信息进行描述

 //2-2用于query检索的命令 —— sqlite3_prepare_v2
 NSString *query = @"SELECT ID , FIELD_DATA FROM FIELDS ORDER BY ROW";
 sqlite3_stmt *statement;
 int result = sqlite3_prepare_v2(database,[query UTF8String],-1,$statement,nil);

//3.取出数据
 




//4.使用绑定变量出入数据
   
  char *sql = "insert into foo values (?,?); ";
  sqlite3_stmt *stmt;
  if(sqlite3_prepare_v2(database, sql,-1,&stmt,nil) == SQLITE_OK){
      sqlite3_bind_int(stmt, 1, 235);
      sqlite3_bind_text(stmt,2,"Text",-1,NULL);
      //("prepare语句的返回值" , "替代问号的索引" , "问号的替代内容" , "上一个参数的传入长度:-1代表字符串长度","可选用的内存清理方式")
  }
  if(sqlite3_step(stmt)!= SQLITE_DONE){
    NSLog(@"Real Error");
  }
  sqlite3_finalize(stmt);
程序中的读、写法
#import <UIKit/UIKit.h>

1.读就是query检所操作
-(void)viewDidLoad{
    


}

2.写就是更新、插入、删除操作






//获取文件路径
-(NSString)dataFilePath{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
    
    NSString *documentDirectory = paths[0];

    return [documentDirectory stringByAppendingPathComponent:@"data.plist"];//指定文件
    return [documentDirectory stringByAppendingPathComponent:@"data.archive"]//体现出用的“归档”的方式
    return [documentDirectory stringByAppendingPathComponent:@"data.sqlite"]//体现出用SQLite3的方式 
}

CoreData


概念

Core Data是水果推荐开发者使用的一种数据化持久方法,为了能够更加准确地理解,也是翻了不少前辈贡献的资料,着实收货很大

CoreData中的核心对象:


iamge

注:黑色表示类名,红色表示类里面的一个属性
1, Managed Object Model
Managed Object Model 是描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。(下文都使用英文术语。)

2,Managed Object Context
Managed Object Context 参与对数据对象(Managed Object)进行各种操作的全过程,并监测数据对象的变化,以提供对 undo/redo 的支持及更新绑定到数据的 UI。

3,Persistent Store Coordinator
Persistent Store Coordinator 相当于数据文件管理器,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。在context的操作下,负责在数据库中生成取出managed object,或者将managed Object存到数据库

4,Managed Object
Managed Object 数据对象,与 Managed Object Context 相关联。

关于Model
模型有点像数据库的表结构,里面包含 Entry, 实体又包含三种 Property:Attribute(属性),RelationShip(关系), Fetched Property(读取属性)。Model class 的名字多以 "Description" 结尾。我们可以看出:模型就是描述数据类型以及其关系的。

主要的 Model class 有:

First Header Second Header Third Header
Managed Object Model NSManagedObjectModel 数据模型
Entity NSEntityDescription 抽象数据类型,相当于数据库中的表
Property NSPropertyDescription Entity 特性,相当于数据库表中的一列
Attribute NSAttributeDescription 基本数值型属性(如Int16, BOOL, Date等类型的属性)
Relationship NSRelationshipDescription 属性之间的关系
Fetched Property NSFetchedPropertyDescription 查询属性(相当于数据库中的查询语句)

和数据库的对应关系

Manged Object <------> Database

Entity <-------> Table

Property <-------> 列 : 包含 Attribute、Relationship、Fetched Property

开发步骤总结:

  1. 初始化NSManagedObjectModel对象,加载模型文件,读取app中的所有实体信息
  2. 初始化NSPersistentStoreCoordinator对象,添加持久化库(这里采取SQLite数据库)
  3. 初始化NSManagedObjectContext对象,拿到这个上下文对象操作实体,进行CRUD操作

这几个对象用通俗的话这样理解:(并且按照下面的顺序理解)

  1. NSManagedObjectModel 确定和哪个应用连接,就是说,这个Core Data是针对哪个app设计的
- (NSManagedObjectModel *)managedObjectModel  
{  
    if (_managedObjectModel != nil) {  
        return _managedObjectModel;  
    }  
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NewsModel" withExtension:@"momd"];  
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];  
    return _managedObjectModel;  
}  

2 . NSPersistentStoreCoordinator 字面意就是持久化存储器,所以,这个对象作用就是定位数据库,就是说,我这个应用用的是哪个数据库,But~~还要指明是哪个应用对吧,哪个应用?这在上面的self.managedObjectModel已经确定了,所以把managedObjectModel引入就可以了。

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator  
{  
    if (_persistentStoreCoordinator != nil) {  
        return _persistentStoreCoordinator;  
    }  
      
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"NewsModel.sqlite"];  
      
    NSError *error = nil;  
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];  
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {  
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);  
        abort();  
    }  
      
    return _persistentStoreCoordinator;  
}  

3 . NSManagedObjectContext 程序中负责操作数据的接口提供者,就是说,用这个对象的实例去CRUD,这之前要指明该对象的实例用的是哪个持久化存储器persistentStore

    
- (NSManagedObjectContext *)managedObjectContext  
{  
    if (_managedObjectContext != nil) {  
        return _managedObjectContext;  
    }  
      
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];  
    if (coordinator != nil) {  
        _managedObjectContext = [[NSManagedObjectContext alloc] init];  
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];  
    }  
    return _managedObjectContext;  
}  
增删查改
//插入数据  
- (void)insertCoreData:(NSMutableArray*)dataArray  
{  
    NSManagedObjectContext *context = [self managedObjectContext];  //都要声明这个负责上下文连接的实例
    for (News *info in dataArray) {  
        News *newsInfo = [NSEntityDescription insertNewObjectForEntityForName:TableName inManagedObjectContext:context];  
        newsInfo.newsid = info.newsid;  
        newsInfo.title = info.title;  
        newsInfo.imgurl = info.imgurl;  
        newsInfo.descr = info.descr;  
        newsInfo.islook = info.islook;  
          
        NSError *error;  
        if(![context save:&error])  
        {  
            NSLog(@"不能保存:%@",[error localizedDescription]);  
        }  
    }  
}  
//查询  
- (NSMutableArray*)selectData:(int)pageSize andOffset:(int)currentPage  
{  
    NSManagedObjectContext *context = [self managedObjectContext];  

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];  
      
          NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];
      
    //setFetchLimit  
    
    //setFetchOffset  
   
    //查找指定日期之后创建的
          NSPredicate * predicate;  
    predicate = [NSPredicate predicateWithFormat:@"creationDate > %@", date];  
    
    //将结果按照title排序                   
    NSSortDescriptor * sort = [[NSortDescriptor alloc] initWithKey:@"title"]; 
    NSArray * sortDescriptors = [NSArray arrayWithObject: sort]; 

    [fetchRequest setEntity:entity];  
    [fetchRequest setFetchLimit:pageSize]; // 限定查询结果的数量 
    [fetchRequest setFetchOffset:currentPage];  // 查询的偏移量  
    [fetchRequest setPredicted:predicate];  // 设置查询条件  
    [fetchRequest setSortDescriptors:sort];  // 设置查询结果的排序方法 
 
    NSError *error;  
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];  
    NSMutableArray *resultArray = [NSMutableArray array];  
    for (News *info in fetchedObjects) {  
        NSLog(@"id:%@", info.newsid);  
        NSLog(@"title:%@", info.title);  
        [resultArray addObject:info];  
    }  
    return resultArray;  
}  
  
//删除  
-(void)deleteData  
{  
    NSManagedObjectContext *context = [self managedObjectContext];  
    NSEntityDescription *entity = [NSEntityDescription entityForName:TableName inManagedObjectContext:context];  
  
    NSFetchRequest *request = [[NSFetchRequest alloc] init];  
    [request setIncludesPropertyValues:NO];  
    [request setEntity:entity];  
    NSError *error = nil;  
    NSArray *datas = [context executeFetchRequest:request error:&error];  
    if (!error && datas && [datas count])  
    {  
        for (NSManagedObject *obj in datas)  
        {  
            [context deleteObject:obj];  
        }  
        if (![context save:&error])  
        {  
            NSLog(@"error:%@",error);    
        }    
    }  
}  
//更新  
- (void)updateData:(NSString*)newsId  withIsLook:(NSString*)islook  
{  
    NSManagedObjectContext *context = [self managedObjectContext];  
  
    NSPredicate *predicate = [NSPredicate  
                              predicateWithFormat:@"newsid like[cd] %@",newsId];  
      
    //首先你需要建立一个request  
    NSFetchRequest * request = [[NSFetchRequest alloc] init];  
    [request setEntity:[NSEntityDescription entityForName:TableName inManagedObjectContext:context]];  
    [request setPredicate:predicate];//这里相当于sqlite中的查询条件,具体格式参考苹果文档  
      
    //https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/Articles/pCreating.html  
    NSError *error = nil;  
    NSArray *result = [context executeFetchRequest:request error:&error];//这里获取到的是一个数组,你需要取出你要更新的那个obj  
    for (News *info in result) {  
        info.islook = islook;  
    }  
      
    //保存  
    if ([context save:&error]) {  
        //更新成功  
        NSLog(@"更新成功");  
    }  

最后


关于几个持久化方法,有几个点需要注意一下:

  • 使用 属性列表 和 归档 还需要考虑将数据存在一个文件还是多个文件中,即单文件持久化(多用)、多文件持久化;
  • 在写文件是用到[myArray writeToFile:filePath atomically:YES] 第二个参数是叫数据写入辅助文件,写完后再将辅助文件复制到第一个参数指明的位置,保障写安全性;
  • 采用属性列表法无法序列化自定义对象,且无法序列化NSURL,UIImage,UIColor等类;
  • 归档能够大多数类进行编解码来实现数据持久化,只要类中的每个属性都是标量或者遵循NSCoding某各类的实例,都可以归档;

done!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容