iOS-CoreData详解与使用

序言

在了解CoreData,大家有必要了解对象关系映射(英语称object Relational Mapping,简称ORM)。

ORM

ORM是通过使用描述对象和数据库之间映射的元数据,可以实现将对象自动持久化到关系数据库当中。ORM的存在为了解决面向对象与数据库科恩干存在不匹配的一种技术。

一 初识CoreData

1.CoreData是一种在iOS 3系统中,也是苹果自己推出的数据存储框架,采用了一种ORM(对象关系映射)的存储关系。

CoreData一个比较大的优势在于在使用CoreData过程中不需要我们编写SQL语句,也就是将OC对象存储于数据库,也可以将数据库数据转为OC对象(数据库数据与OC对象相互转换)。

2.CoreData几个类

(1)NSManagedObjectContext

托管对象上下文,数据库的大多数操作是在这个类操作

(2)NSManagedObjectModel

托管对象模型,其中一个托管对象模型关联到一个模型文件,里面存储着数据库的数据结构。

(3)NSPersistentStoreCoordinator

持久化存储协调器,主要负责协调上下文玉存储的区域的关系。

(4)NSManagedObject

托管对象类,其中CoreData里面的托管对象都会继承此类。

三 CoreData基本使用
1.gif

下面开始讲解CoreData的基本使用,里面会涉及到源码,内容比较多,希望大家静下来看完。

使用CoreData方式,有两种可能。第一种是项目开始就创建带有CoreData数据库,还有一种项目已经开始了,重新接入CoreData,下面我们第三部分主要讲述这两种方式的过程。

1.项目开始就使用CoreData

我们在创建项目的时候,勾选Use Core Data

image.png

然后在文件目录中自动生成一个后缀为.xcdatamodeld的文件

image.png

打开AppDelegate发现类中多了以下内容

  • AppDelegate.h
@property (readonly, strong) NSPersistentContainer *persistentContainer;

- (void)saveContext;
  • AppDelegate.m
@synthesize persistentContainer = _persistentContainer;

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Replace this implementation with code to handle the error appropriately.
                    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                    */
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    
    if ([context hasChanges] && ![context save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}

我们可以点开testCoreData.xcdatamodeld文件,我们可以看到实体和关系。如下图

image.png

图表说明

  • Add Entity 添加表
  • Add Attrubte 添加属性

(1)
image.png

创建后可以清楚的看到模型文件左侧的列表,有三个Entities、Fetch Requests以及Configurations三个选项。

  • ENTITIES 实体
  • FETCH REQUESTS 请求模版
  • CONFIGURATIONS 配置信息

(2)
image.png

添加完一个实体后,你会发现一个实体是对应着三个内容,分别是Attributes、Relationships和Fetched Properties。

  • Attributes 属性
  • Relationships 关联关系
  • Fetched Properties 获取操作

(3)实体属性类型

image.png

我们来分别简单解释类型的意义,从上往下

  • Undefined:也就是默认值,如果参与编译会报错
  • Integer 16:代表整数,范围是-32768 ~ 32767
  • Integer 32:代表整数,范围是-2147483648 ~ 2147483647
  • Integer 64:代表整数,范围是–9223372036854775808 ~ 9223372036854775807,还是很大的,较少用
  • Double:代表小数
  • Float:代表小数
  • String:代表字符串,NSString表示
  • Boolean:代表布尔值,使用NSNumber表示
  • Date:代表日期时期
  • Binary Data:代表二进制,是用NSData表示
  • Transformable:代表Objective对象,要遵守NSCoding协议

(4)关联关系

image.png

点击加号,可以添加关联关系,在inverse这个属性代表两个实体在Relationships设置关联关系后之后,是否可以从一个实体中找到另一个实体,这样使两个实体具有双向的关联关系。

(5)Editor Style

image.png

大家通过点击下面红色按钮,style按钮可以看出实体和属性的关系,以及可以看出实体之间的对应的关系。

上面是coreData的视图的基本运用,自己也是一个不断摸索的过程,下面讲述CoreData的基本操作。

三 CoreData基本使用

在讲述操作之前,我们首先讲述NSManagedObjectContext,苹果推荐使用initWithConcurrencyType方式创建,在创建时,指定当前是什么类型的并发队列,参数也是一个枚举值。

NSManagedObjectContext枚举值参数有三个类型:

  • NSConfinementConcurrencyType:此类型在iOS9之后被苹果弃用,所以不建议用这个API。
  • NSPrivateQueueConcurrencyType:代表私有并发队列的类型,操作也是在子线程中完成的。
  • NSMainQueueConcurrencyType:代表主并发队列类型,如果在操作过程中,需要涉及到UI操作,则应该使用这个参数初始化上下文完成操作。

创建NSManagedObjectContext一共有两种方法,下面分别介绍

1.下面我们以Student为模型,创建主队列并发类型的NSManagedObjectContext
/** NSManagedObjectContext */
@property(nonatomic, strong)NSManagedObjectContext *context;

- (NSManagedObjectContext *)context {
    if (_context == nil) {
        // 创建上下文对象,并发队列设置为主队列
        _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        
        // 创建托管对象模型,并使用Student.momd路径当做初始化参数
        // .xcdatamodeld文件 编译之后变成.momd文件  (.mom文件)
        NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreDataDemo" withExtension:@"momd"];
        NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
        
        // 创建持久化存储调度器
        NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
        
        // 创建并关联SQLite数据库文件,如果已经存在则不会重复创建
        NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
        dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite",@"Student"];
        
        [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
        
        // 上下文对象设置属性为持久化存储器
        _context.persistentStoreCoordinator = coordinator;
    }
    return _context;
}
  • 解释说明

(1) 将NSManagedObjectContext声明为一个变量,并且使用懒加载的形式,方便使用。

(2) .xcdatamodeld文件 编译之后变成.momd文件,所以生成NSManagedObjectModel托管对象模型的时候,文件后缀名一定要写对。

image.png

(3) 创建并关联SQLite数据库文件,如果已经存在则不会重复创建。


image.png
2.使用系统推荐的方法创建
@property (readonly, strong) NSPersistentContainer *persistentContainer;

@synthesize persistentContainer = _persistentContainer;

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

使用的时候

self.persistentContainer.viewContext
四 实战 - 增,删,改,查
4.1 插入操作

CoreData通过NSEntityDescriptioninsert进行插入操作,这样就会生成并返回一个托管对象,并将这个对象插入到上下文中。下面以一个Student为例:

- (Student *)insert1 {
    NSError *error = nil;
    
   // 开始创建托管对象,并指明好创建的托管对象所属实体名
    Student * student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
    
    //2.根据表Student中的键值,给NSManagedObject对象赋值
    student.name = [NSString stringWithFormat:@"Mr-%d",arc4random() % 100];
    student.age = arc4random() % 20;
    student.sex = arc4random() % 2 == 0 ?  @"男" : @"女" ;
    student.height = arc4random() % 180;
    student.number = arc4random() % 100;
    
    if ([self.context hasChanges] && ![self.context save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        _resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.userInfo];
        abort();
        return nil;
    } else {
        _resultLbe.text = @"插入数据成功";
    }
    
    return student;
}

NSManagedObjectContext将操作的数据放到了缓存层中,只有调用了NSManagedObjectContextsave后,才会对数据库进行真正的操作,否则对象仅仅存在内存中,这样就很好地避免了数据库的频繁访问。

4.2 删除操作

CoreData首先通过获取需要删除的托管对象,遍历所需要获取的对象数组,逐个删除,最后调用NSManagedObjectContextsave方法。

- (void)delete:(NSArray *)delStudents {
    // 获取数据的请求对象,指明对实体进行删除操作
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    NSMutableArray *delStudentSucces = [NSMutableArray array];  // 保存在数据库中成功被删除的对象
    [delStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
        // 通过创建谓词对象,然后过滤掉符合要求的对象,也就是要删除的对象
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
        request.predicate = predicate;
        
        // 通过执行获取操作,找到要删除的对象即可
        NSError *error = nil;
        NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
        
        // 开始真正操作,一一遍历,遍历符合删除要求的对象数组,执行删除操作
        [students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
            [self.context deleteObject:obj];
        }];
        
        // 错误处理
        if (error) {
            self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
        } else {
            [delStudentSucces addObject:obj];
        }
    }];
    
    // 最后保存数据,保存上下文。
    if (self.context.hasChanges) {
        [self.context save:nil];
    }
    
    if (delStudentSucces.count > 0) {
        self.resultLbe.text = @"删除数据成功";
    }
    
    // 将已经在数据库中被删除的对象从内存中移除
    [delStudentSucces enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
        [self.dataSources removeObject:obj];
    }];
    [self.tableView reloadData];
}
4.3 修改操作

将所有数据库中的学生,性别颠倒,年龄加1操作。

- (void)modify:(NSArray *)modifyStudents {
    // 获取数据的请求对象,指明对实体进行删除操作
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    NSMutableArray *modifyStudentSucces = [NSMutableArray array];
    [modifyStudents enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
        // 通过创建谓词对象,然后过滤掉符合要求的对象,也就是要删除的对象
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number = %d",obj.number];
        request.predicate = predicate;
        
        // 通过执行获取操作,找到要删除的对象即可
        NSError *error = nil;
        NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
        
        // 开始真正操作,一一遍历,遍历符合删除要求的对象数组,执行删除操作
        [students enumerateObjectsUsingBlock:^(Student *obj, NSUInteger idx, BOOL *stop) {
            obj.age += 1;
            obj.sex = [obj.sex isEqualToString:@"男"] ? @"女" : @"男";
        }];
        
        // 错误处理
        if (error) {
            self.resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
        } else {
            [modifyStudentSucces addObject:obj];
        }
    }];
    
    // 最后保存数据,保存上下文。
    if (self.context.hasChanges) {
        [self.context save:nil];
    }
    
    if (modifyStudentSucces.count > 0) {
        self.resultLbe.text = @"修改数据成功";
    }
    
    NSArray *news = [self search];
    [self.dataSources removeAllObjects];
    [self.dataSources addObjectsFromArray:news];
    [self.tableView reloadData];
    
    self.resultLbe.text = @"修改数据成功";
}
4.4 查找操作

查找操作是是有许多条件限制,根据条件查找出相应的数据,下面以一个例子说明一下(查找出所有的元素,条件以后细节会讲出)

- (NSArray *)search {
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    
    // 执行获取操作,获取所有Student托管对象
    NSError *error = nil;
    NSArray<Student *> *students = [self.context executeFetchRequest:request error:&error];
    
    if (error) {
        _resultLbe.text = [NSString stringWithFormat:@"CoreData Error:%@",error.description];
    } else {
        _resultLbe.text = @"查找数据成功";
    }
    
    return students;
}

以上就是CoreData的基本使用,自己也在不断的完善中,希望上面对大家对CoreData认识会进一步提高。


本文参考 iOS-CoreData详解与使用 ,非常感谢该作者。


更多同类型文章参考
iOS-SQLite3的使用详解
iOS-CoreData详解与使用
iOS-FMDB详解及使用
iOS SQLite、CoreData、FMDB数据库详解


项目连接地址 - CoreDataDemo

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

推荐阅读更多精彩内容

  • 该文章属于刘小壮原创,转载请注明:刘小壮[https://www.jianshu.com/u/2de707c93d...
    刘小壮阅读 19,761评论 17 149
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,092评论 1 32
  • 1 CoreData运行机制 1.1 CoreData总体架构 1.1.1 CoreData架构图 CoreDat...
    Kevin_Junbaozi阅读 3,615评论 2 7
  • 在今年清华经管学院举行的2017级本科生开学典礼上,清华经管学院院长钱颖一教授和新同学们分享了乔布斯讲的三个故事。...
    EC君_王磊阅读 177评论 0 0
  • 久违的雨 淅淅沥沥 窗外 斜织的雨丝 洗礼着路旁的绿树 花草也打起精神 享受着迟来的甘霖 来往的行人中 那个花伞底...
    张爱国_3a01阅读 149评论 0 1