前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >储存篇 - CoreData使用大全

储存篇 - CoreData使用大全

作者头像
進无尽
发布2019-01-02 12:50:13
2.5K0
发布2019-01-02 12:50:13
举报
文章被收录于专栏:進无尽的文章

【一】前言

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赋值,也可以使用 . 属性 的方式直接赋值。
代码语言:javascript
复制
    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、搭建上下文环境

代码语言:javascript
复制
//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、增:增加数据

代码语言:javascript
复制
/**
 增加数据
 */
-(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、删:删除数据

代码语言:javascript
复制
/**
删除数据
*/
-(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、改:修改数据

代码语言:javascript
复制
/**
 修改数据
 */
-(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、查:查询数据

代码语言:javascript
复制
/**
 查询数据
 */
-(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 中添加好实体即可,然后就可以直接使用了。
代码语言:javascript
复制
    #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,值如下:
代码语言:javascript
复制
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

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

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

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

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

代码语言:javascript
复制
pod "MagicalRecord"
很多操作在这份MagicalRecord中文文档中都说明的很清楚,这里作简单归纳总结
1、AppDelegate中的设置
代码语言:javascript
复制
#import <MagicalRecord/MagicalRecord.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [MagicalRecord setupCoreDataStack];
    // ...
    return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application
{
    [MagicalRecord cleanUp];
}
2、对象的储存和查询
代码语言:javascript
复制
  #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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.12.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【一】前言
  • 【二】各种类作用的介绍
  • 【三】手动创建CoreData数据
  • 【四】使用系统自动创建的CoreData
  • 【五】关于CoreData的版本迁移
  • 【六】CoreData第三方库:MagicalRecord
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档