专栏首页進无尽的文章储存篇 - CoreData使用大全

储存篇 - CoreData使用大全

【一】前言

Core Data框架提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite3数据库文件中,也能够将保存在数据库中的数据还原成OC对象。在此数据操作期间,不需要编写任何SQL语句。使用此功能,要添加CoreData.framework和导入主头文件 <CoreData/CoreData.h>。

【二】各种类作用的介绍

创建Core Data Stack

  • iOS10中利用NSPersistentContainer
  • iOS10之前涉及NSManagedObjectContext、NSPersistentStoreCoordinator、NSManagedObjectModel、NSPersistentStore这些类

【三】手动创建CoreData数据

我们创建一个和平常一样的工程,不需要勾选Use Core Data:

一、创建模型文件

1、进入创建新文件,command+N或者如下图

2、选择文件类型, 如下图:

3、设置文件名,如下图:

4、模型文件创建成功,会出现以后

建好后你会发现工程中多了 XXXXXXX.xcdatamodeld,我们需要在这里添加实体(首字母大写)和实体的属性。

二、创建实体

1、利用可视化的方式创建实体,实体的功能就类似于我们的Model类,具体操作如下如:

在传统的项目中我们都使用OC变成,但是CoreData默认使用的是Swift语言,所以我们要设置回来OC,详情见图片

同时需要将codegen选为Manaul/None

image.png

这里我们需要创建Person和Card的实体以及实体属性:

实体间的关系:选中Person实体,在Person中添加card属性:

image.png

选中Card实体,在Card中添加person属性:

添加完成后,他们关系如下:

三、创建实体类

利用可视化创建了实体,但是我们要想获取对应的数据和名称,就必须关联类,因此要创建实体类,创建步骤如下:

1、选中 .xcdatamodeld 文件通过 Editor 创建:NSManagedObject subclass类文件

2、生成了4个分类 分别为A+CoreDataClass.h, A+CoreDataClass.m, A+CoreDataProperties.h,A+CoreDataProperties.m 前2个为正式类文件(可以在需要用的地方直接引用这个类,这个类内部已经引用了后面两个类), 后两个为属性类文件。

四、手动创建CoreData的使用
值得注意的是:下面的例子中我们可以直接使用创建的目的实体类如:Dog,也可以使用NSManagedObject 这一公共实体类,可以使用KVC赋值,也可以使用 . 属性 的方式直接赋值。
    NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:_context];
    [person setValue:@"lifengfeng" forKey:@"name"];
    [person setValue:[NSNumber numberWithInt:23] forKey:@"age"];
   
   +++++++++++++++++   另一个方式  +++++++++++++++++
    Dog *dog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog" inManagedObjectContext:self.myContext];
    dog.name = @"name1";
    dog.age = @"12";

    for (Dog *obj in objs) {
        NSLog(@"name=%@", obj.name);
    }

1、搭建上下文环境

//1、创建模型对象
    //获取模型路径
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NewCodeDataModel" withExtension:@"momd"];//NewCodeDataModel.xcdatamodeld
    //根据模型文件创建模型对象
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    //2、创建持久化助理
    //利用模型对象创建助理对象
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    //数据库的名称和路径
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"mySqlite.sqlite"];
    NSLog(@"path = %@", sqlPath);
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    //设置数据库相关信息
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:nil];
    
    //3、创建上下文
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    //关联持久化助理
    [context setPersistentStoreCoordinator:store];
    self.myContext = context;

   其中
   持久化存储库的类型(addPersistentStoreWithType:参数):
  (1)NSSQLiteStoreType  SQLite数据库
  (2)NSBinaryStoreType  二进制平面文件
  (3)NSInMemoryStoreType 内存库,无法永久保存数据

   ConcurrencyType可选项(initWithConcurrencyType:参数):
  (1)NSConfinementConcurrencyType 这个是默认项,每个线程一个独立的Context,主要是为了兼容之前的设计。
  (2)NSPrivateQueueConcurrencyType 创建一个private queue(使用GCD),这样就不会阻塞主线程。
  (3)NSMainQueueConcurrencyType 创建一个main queue,使用主线程,会阻塞。

2、增:增加数据

/**
 增加数据
 */
-(void)addData{
    
    //传入上下文,创建一个Person实体对象:
    NSManagedObject *person =
    [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:_context];
    
    //设置简单属性:
    [person setValue:@"lifengfeng" forKey:@"name"];
    [person setValue:[NSNumber numberWithInt:23] forKey:@"age"];
    
    //传入上下文,创建一个Card实体对象:
    NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:_context];
    [card setValue:@"1234567890" forKey:@"no"];
    
    //设置Person和Card之间的关联关系:
    [person setValue:card forKey:@"card"];
    
    //利用上下文对象,将数据同步到持久化存储库:
    NSError *error = nil;
    BOOL success = [_context save:&error];
    if (!success) {
        [NSException raise:@"访问数据库错误!" format:@"%@", [error localizedDescription]];
    }else{
        NSLog(@"访问数据库成功!");
    }
    
    // 如果是想做更新操作:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库
}

3、删:删除数据

/**
删除数据
*/
-(void)deleteData{
   
   //建立请求,连接实体
   NSFetchRequest *request = [[NSFetchRequest alloc] init] ;
   NSEntityDescription *person = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:_context];
   request.entity = person;
   
   //设置条件过滤(搜索name属性中包含”lifengfeng“的那条记录,注意等号必须加,可以有空格,也可以是==)
   NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@", @"lifengfeng"];
   request.predicate = predicate;
   
   //遍历所有实体,将每个实体的信息存放在数组中
   NSArray *arr = [_context executeFetchRequest:request error:nil];
   
   //删除并保存
   if(arr.count)
   {
       for (NSEntityDescription *p in arr)
       {
           [_context deleteObject:p];
           NSLog(@"删除%@成功!",p.name);
       }
       //保存
       [_context save:nil];
   }
}

4、改:修改数据

/**
 修改数据
 */
-(void)updateData{
    //建立请求,连接实体
    NSFetchRequest *request = [[NSFetchRequest alloc] init] ;
    NSEntityDescription *person = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:_context];
    request.entity = person;
    
    //设置条件过滤(搜索所有name属性不为“lifengfeng”的数据)
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name!=%@", @"lifengfeng"];
    request.predicate = predicate;
    
    //遍历所有实体,将每个实体的信息存放在数组中
    NSArray *arr = [_context executeFetchRequest:request error:nil];
    
    //更改并保存
    if(arr.count)
    {
        for (NSEntityDescription *p in arr)
        {
            p.name = @"更改";
        }
        //保存
        [_context save:nil];
    }
    else
    {
        NSLog(@"无检索");
    }
}

5、查:查询数据

/**
 查询数据
 */
-(void)queryData{
    
    //初始化一个查询请求:
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    
    //设置要查询的实体:
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:_context];
    request.entity = entity;
    
    //设置排序(按照age降序):
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
    request.sortDescriptors = [NSArray arrayWithObject:sort];
    
    //设置条件过滤(name like '%lifengfeng%'):
    //设置条件过滤时,数据库里面的%要用*来代替
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*lifengfeng*"];
    request.predicate = predicate;
    //执行请求:
    NSError *error = nil;
    NSArray *objs = [_context executeFetchRequest:request error:&error];
    if (error) {
        [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
    }
    //遍历数据:
    for (NSManagedObject *obj in objs) {
        NSLog(@"name=%@", [obj valueForKey:@"name"]);
    }
}

【四】使用系统自动创建的CoreData

系统帮我们在AppDelegate中创建了一个NSPersistentContainer实例,以及一个saveContext方法。(并且已经帮我们创建了.xcdatamodeld模型文件) 注意看saveContext,我们通过NSPersistentContainer的属性viewContext拿到NSManagedObjectContext对象,再通过save:方法进行数据的保存。 因为系统并没有帮我们适配旧系统,所以如果App要在非iOS10的旧系统运行,还需要做类似上面 “搭建上下文环境”的工作,因为那里的代码在iOS10以下和以上的代码中都可以执行。 如果是Xcode8之前的版本自动创建的Core Data Stack,会不一样(跟情况2类似),如下图:

一个大坑:

这里有个坑,在Xcode8中,Codegen下拉选择框中增加了Class/Definition这一选项,而且是默认的预设值,这时候系统会自动帮我们这个实体创建了NSManagedObject子类,我们不需要再创建实体类,最坑的是,这些自动创建的类,在导航面板是看不见的!!!然后你很容易再重复手动创建NSManagedObject子类,这时候就会报类似「duplicate symbol _OBJC_METACLASS_Photography in:...」这类错误。 所以,如果你想自己手动创建NSManagedObject子类,就要把系统预设的Class/Definition改为Manual/None。

使用系统自动创建的CoreData时,非常的方便,我们只需要在 xxxxx.xcdatamodeld 中添加好实体即可,然后就可以直接使用了。

    #import "Man+CoreDataClass.h"
    #import "AppDelegate.h"

    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSPersistentContainer *container = delegate.persistentContainer;
    Man *man = [NSEntityDescription insertNewObjectForEntityForName:@"Man" inManagedObjectContext:container.viewContext];
    man.name = @"小明";
    man.height = @"180";
    
    // ++++++++ 保存数据 ++++++++
    NSError *error = nil;
    BOOL success = [container.viewContext save:&error];
    if (!success) {
        [NSException raise:@"访问数据库错误!" format:@"%@", [error localizedDescription]];
    }else{
        NSLog(@"访问数据库成功!");
    }
    // ++++++++ 查询数据 ++++++++
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    //设置要查询的实体:
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Man" inManagedObjectContext:container.viewContext];
    request.entity = entity;
    NSError *error1 = nil;
    NSArray *objs = [container.viewContext executeFetchRequest:request error:&error];
    if (error1) {
        [NSException raise:@"查询错误" format:@"%@", [error1 localizedDescription]];
    }
    //遍历数据:
    for (NSManagedObject *obj in objs) {
        NSLog(@"name=%@", [obj valueForKey:@"name"]);
    }

【五】关于CoreData的版本迁移

应用场景:修改了实体的数据结构(比如说某个实体增加了一个特性),因为APP版本更新后沙盒中的NSDocumentDirectory 中的缓存数据都不会被清除,这时候就要进行版本迁移了,否则已经安装旧App的手机,在更新应用后,两边数据结构不一致导致不能识别,会崩溃。 步骤:

  • 选中.xcdatamodeld文件,Editor > Add Model Version,创建一个新版的.xcdatamodeld文件
  • 切换到新版的.xcdatamodeld文件(切换成功后会有绿色的勾),如下图:
  • 对.xcdatamodeld文件进行你想要的修改
  • 创建NSPersistentStore的时候,options参数传一个dictionary,值如下:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
    [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

 //在初始化的时候用到了版本迁移的设置
- (void)initializeCoreDataLessThaniOS10 {
    // Get managed object model(拿到模型文件,也就是.xcdatamodeld文件(我们会在初始化完Core data Stack后创建))
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MoveBand" withExtension:@"momd"];
    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    NSAssert(mom != nil, @"Error initalizing Managed Object Model");
    
    // Create persistent store coordinator(创建NSPersistentStoreCoordinator对象(需要传入上述创建的NSManagedObjectModel对象))
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
    
    // Creat managed object context(创建NSManagedObjectContext对象(_context是声明在.h文件的属性——因为其他类也要用到这个属性))
    _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    // assgin persistent store coordinator(赋值persistentStoreCoordinator)
    _context.persistentStoreCoordinator = psc;
    
    // Create .sqlite file(在沙盒中创建.sqlite文件)
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];
    
    // Create persistent store(异步创建NSPersistentStore并add到NSPersistentStoreCoordinator对象中,作用是设置保存的数据类型(NSSQLiteStoreType)、保存路径、是否支持版本迁移等)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        // 用于支持版本迁移的参数
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                 [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
        NSError *error = nil;
        NSPersistentStoreCoordinator *psc = _context.persistentStoreCoordinator;
        
        // 备注,如果options参数传nil,表示不支持版本迁移
        NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
                                                     configuration:nil
                                                               URL:storeURL
                                                           options:options
                                                             error:&error];
        NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
    });
}

最后值得注意的是:Core Data的延迟加载

Core Data不会根据实体中的关联关系立即获取相应的关联对象,比如通过Core Data取出Person实体时,并不会立即查询相关联的Card实体;当应用真的需要使用Card时,才会查询数据库,加载Card实体的信息。

【六】CoreData第三方库:MagicalRecord

地址:MagicalRecord

CoreData是苹果自家推出的一个持久化框架,使用起来更加面向对象。但是在使用过程中会出现大量代码,
而且CoreData学习曲线比较陡峭,如果掌握不好,在使用过程中很容易造成其他问题。

国外开发者开源了一个基于CoreData封装的第三方——MagicalRecord,就像是FMDB封装SQLite一样,
MagicalRecord封装的CoreData,使得原生的CoreData更加容易使用。并且MagicalRecord降低了CoreData的使用门槛,
不用去手动管理之前的PSC、MOC等对象。

添加MagicalRecord到项目中

MagicalRecord添加到项目中,和使用其他第三方一样,可以通过下载源码和CocoaPods两种方式添加。 但是不推荐直接拖源码到项目中,一是需要自己管理代码更新,另一个原因是,直接拖源码进项目是会报错的,修改起来很麻烦。

推荐通过CocoaPods安装MagicalRecord,需要在Podfile中加入下面命令,后续只需要通过命令来更新。

pod "MagicalRecord"

很多操作在这份MagicalRecord中文文档中都说明的很清楚,这里作简单归纳总结

1、AppDelegate中的设置

#import <MagicalRecord/MagicalRecord.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [MagicalRecord setupCoreDataStack];
    // ...
    return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application
{
    [MagicalRecord cleanUp];
}

2、对象的储存和查询

  #import <MagicalRecord/MagicalRecord.h>

    // 获取上下文环境
    NSManagedObjectContext *localContext    = [NSManagedObjectContext MR_context];
    // 在当前上下文环境中创建一个新的 Person 对象.
    Man *person  = [Man MR_createEntityInContext:localContext];
    person.name = @"MagicalRecord存储";
    // 保存修改到当前上下文中.
    [localContext MR_saveToPersistentStoreAndWait];
    
    NSArray *peopleArray = [Man MR_findAll];
    for (Man *man in peopleArray) {
        NSLog(@"名称:%@",man.name);
    }

对于MagicalRecord 的使用感受就是,确实如它的名称一样,如此简洁和方便的实现了对象的增删改查,如此的充满魔力,关于的它的更多使用可以参考上面的中文文档,相信大家都可以熟练掌握这个好用的类库!

另外还有其他的第三方存储库: 可以存对象的数据库realm-cocoa使用时参考这篇文章:移动端数据库新王者:realm


参考文章: iOS 开发之 CoreData CoreData的使用 iOS CoreData数据库之创建详解 「死磕」Core Data——入门 认识CoreData - 初识CoreData

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Swift实践:使用CoreData完成上班签到小工具1. CoreData Stack的作用2.创建 CoreData Stack3. 一对多的关系4. 完成Demo,了解使用CoreData St

    stanbai
  • Swift实践:使用CoreData完成一个通讯录存储

    stanbai
  • Swift实践:使用CoreData存储多种数据类的通讯录1. CoreData支持存储数据类型2. 使用CoreData存储多种数据类的通讯录3. Codable

    stanbai
  • IOS开发之记录用户登陆状态

      上一篇博客中提到了用CoreData来进行数据的持久化,CoreData的配置和使用步骤还是挺复杂的。但熟悉CoreData的使用流程后,CoreData还...

    lizelu
  • iOS中CoreData数据管理系列三——添加与查询数据

        在前两篇博客中,分别介绍了iOS中CoreData框架创建数据模型和CoreData框架中的三个核心类。博客地址如下:

    珲少
  • iOS开发之使用XMPPFramework实现即时通信(二)

    上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramewor...

    lizelu
  • iOS开发之表视图爱上CoreData

      在接触到CoreData时,感觉就是苹果封装的一个ORM。CoreData负责在Model的实体和sqllite建立关联,数据模型的实体类就相当于Java中...

    lizelu
  • iOS开发常用之数据库、缓存处理

    GuangdongQi
  • iOS CoreData (一) 增删改查

    选择Arguments,在下面的ArgumentsPassed On Launch中添加下面两个选项,如图:

    且行且珍惜_iOS

扫码关注云+社区

领取腾讯云代金券