[iOS]Core Data浅析三 -- 数据的增删改查

Core Data系列文章:
[iOS]Core Data浅析一 -- 启用Core Data
[iOS]Core Data浅析二 -- 转换实体(Entity)为模型对象
[iOS]Core Data浅析三 -- 数据的增删改查
以及一个, 在新版Xcode中使用的注意事项:
[Core Data]Xcode 8+ 新建NSManageObject subclass方法

之前的文章讲解了项目中启用CoreData以及将创建的实体(Entity)转换为模型对象, 今天就来看下实际使用中对数据的增删改查.

基本的增删改查

coreData的插入数据主要是使用了NSEntityDesctiption类的这个方法:

+ (__kindof NSManagedObject *)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;

两个参数分别为: 实体(Entity)的名称, 和当前的上下文;
例如:

//获取代理
    AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    //获取context
    NSManagedObjectContext *context = [delegate managedObjectContext];
    
    //获取PeopleEntity实体
    PeopleEntity *people = [NSEntityDescription insertNewObjectForEntityForName:@"PeopleEntity" inManagedObjectContext:context];

//设置属性内容
    people.name = @"流火绯瞳";
    people.age = @27;
    people.sex = @0;

NSError *error;
    //保存更改
    if ([context save:&error]) {
        NSLog(@"保存成功");
    } else {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }

在最后调用save方法的时候, 才是真正将数据插入到数据库中;

因为一般的删改的前提是需要查询出来, 所以先讲如何查;
从数据库中查询数据,会用到三个类:NSFetchRequestNSPredicateNSSortDescriptor

  • NSFetchRequest : 查询请求
  • NSPredicate : 谓词, 查询条件

  • NSSortDescriptor : 查询结果的排序方式

  • NSFetchRequest 实例常用属性:

  • entity : 待查询的实体
  • predicate : 查询条件
  • sortDescriptors : 排序规则, 是一个数组, 含有多个NSSortDescriptor实例, 优先级按在数组中的先后顺序
  • resultType: 获取的结果类型, 是个枚举, 默认NSManagedObjectResultType; 一般我们获取的是NSManagedObject对象, 使用默认值即可, 不需要设置
typedef NS_OPTIONS(NSUInteger, NSFetchRequestResultType) {
    NSManagedObjectResultType       = 0x00,
    NSManagedObjectIDResultType     = 0x01, 
    NSDictionaryResultType          API_AVAILABLE(macosx(10.6), ios(3.0)) = 0x02,
    NSCountResultType               API_AVAILABLE(macosx(10.6), ios(3.0)) = 0x04
};
  • fetchLimit : 指定结果集中数据的最大数目

  • fetchOffset : 查询的偏移量, 默认为0, 从头开始查询

  • fetchBatchSize : 批处理查询的一次查询的大小, 设置后, 结果会分批返回

  • propertiesToGroupBy : 指定分组规则

  • propertiesToFetch : 指定查询的字段, 是个数组, 默认查询全部字段

  • NSSortDescriptor
    具体可参考本人另一篇文章[iOS] 浅析排序规则描述类: NSSortDescriptor

一个查询的示例代码如下:

//创建一个查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PeopleEntity" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    // Specify criteria for filtering which objects to fetch
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", @"name", @"流火"];// 查询属性name中包含 流火 的数据
    [fetchRequest setPredicate:predicate];
    // Specify how the fetched objects should be sorted
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age"
                                                                   ascending:YES];// 按age 升序排序
    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
    
    NSError *error = nil;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"查询失败: %@", error.description);
    } else {
        for (PeopleEntity *info in fetchedObjects) {
            
            NSLog(@"Name: %@", info.name);
            NSLog(@"age: %@", info.age);
            NSLog(@"sex: %@", info.sex);
            NSLog(@"-----------------------------------");
            
            ManEntity *man1 = [info valueForKey:@"manRelationship"];
            
            
            NSLog(@"Name: %@", man1.name);
            NSLog(@"weight: %@", man1.weight);
            NSLog(@"height: %@", man1.height);
            NSLog(@"==========================================");
        }
    }

删除操作, 相对来说就比较简单了, 只需要调用 ** NSManagedObjectContext** 的方法:

- (void)deleteObject:(NSManagedObject *)object;

需要查询出待删除的实体, 然后调用这个方法删除即可; 例如, 如果把上面查询出来的PeopleEntity实体删除, 可在查询结果中这么做:

for (PeopleEntity *info in fetchedObjects) {

           [context deleteObject:info]; 
   }

// 最后一定要调用save方法:
NSError *error;
    //保存更改
    if ([context save:&error]) {
        NSLog(@"保存成功");
    } else {
        NSLog(@"失败: %@", [error localizedDescription]);
    }

进行删除操作后, 一定要调用save方法, 来使删除生效;

类似于删除的方法, 修改的方法, 也是在查询的结果之上进行操作的:

for (PeopleEntity *info in fetchedObjects) 
        // 把所有的name改为 张三
        info.name = @"张三";
}

// 最后一定要调用save方法:
NSError *error;
    //保存更改
    if ([context save:&error]) {
        NSLog(@"保存成功");
    } else {
        NSLog(@"失败: %@", [error localizedDescription]);
    }

最后调用save方法, 来使更改生效即可;

以上便是最简单的增删改查操作, 会发现, 在进行删改操作的时候有许多不便之处, 需要将数据查询出来加载到内存中, 才能去操作, 这在数据量比较大的时候, 是不可取的, 针对这个问题, 苹果提供了批量操作的API.

批量操作

批量增

批量插入, 只能以循环的方式, 将数据逐条保存到数据库, 例如 插入100条数据:

for(int i=0; i<100;i++) {

//获取PeopleEntity实体
    PeopleEntity *people = [NSEntityDescription insertNewObjectForEntityForName:@"PeopleEntity" inManagedObjectContext:context];

//设置属性内容
    people.name = @"流火绯瞳";
    people.age = @27;
    people.sex = @0;

NSError *error;
    //保存更改
    if ([context save:&error]) {
        NSLog(@"保存成功");
    } else {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }
}

批量改

方式一

这个方法, 还是需要将要修改的数据查询出来放到一个数组里, 例如: peoples数组, 然后使用集合的KVC特性,例如修改所有数据姓名为: 流火:

[peoples setValue:@"流火" forKeyPath:@"name"];
方式二: NSBatchUpdateRequest

这个方法使用的是NSBatchUpdateRequest, 这是苹果在iOS8, MacOS10.10之后新加的API, 这个方法的好处是: 不需要将数据查询出来, 也不需要加载数据到内存中, 而是直接更新的数据库中的数据; 当然, 他也有一个不好地方, 就是在进行此操作之前查询出的数据就不是最新的了, 没有和数据库同步; 解决办法就是调用** NSManagedObjectContext**的这个方法来告诉 context,有哪些数据更新了:

+ (void)mergeChangesFromRemoteContextSave:(NSDictionary*)changeNotificationData intoContexts:(NSArray<NSManagedObjectContext*> *)contexts;

参数:

  • changeNotificationData :
    字典, key有三个:
    NSUpdatedObjectsKey : 更新
    NSInsertedObjectsKey : 插入
    NSDeletedObjectsKey : 删除
    value 为包含NSManagedObjectID的数组, 或者NSURL objects conforming to valid results from -URIRepresentation
  • contexts : 数组
    作用到的context

NSBatchUpdateRequest常用属性:

  • predicate : 谓词, 指定更新条件
  • propertiesToUpdate : 指定需要更新的属性和值; 是一个字典: key是属性名称, value是要更新的值, 如果是基本数据类型需要转换为NSNumber对象
  • resultType: 返回结果, 是一个枚举, 有三个值
NSStatusOnlyResultType // Return a status boolean
NSUpdatedObjectIDsResultType// Return the object IDs of the rows that were updated
NSUpdatedObjectsCountResultType // Return the number of rows that were updated
  • affectedStores : 当前更改影响到的存储器, 可设置为当前context的所有存储器: context.persistentStoreCoordinator.persistentStores

批量更新示例代码:

//获取代理
    AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    //获取context
    NSManagedObjectContext *context = [delegate managedObjectContext];
    
    // 这里使用另外一种获取实体的方法
//    PeopleEntity *people = (PeopleEntity*)[PeopleEntity entity];
//    //根据Entity创建
//    NSBatchUpdateRequest *update = [[NSBatchUpdateRequest alloc]initWithEntity:people];
    // 或者根据entityName来创建
    
    NSBatchUpdateRequest *update = [[NSBatchUpdateRequest alloc]initWithEntityName:@"PeopleEntity"];
    // 修改所以age小于30的
    update.predicate = [NSPredicate predicateWithFormat:@"age < 30"];
    // 将姓名修改为张三, age修改为31
    update.propertiesToUpdate = @{@"name": @"张三", @"age": @31};
    // 因为在后面调用mergeChangesFromRemoteContextSave方法来同步更新的时候, 需要使用更改数据的ID, 所以这里设置返回值为ID
    update.resultType = NSUpdatedObjectIDsResultType;
    //设置此次更新作用到的存储器
//    update.affectedStores = context.persistentStoreCoordinator.persistentStores;
    
    // 执行更新操作
    NSError *error;
    NSBatchUpdateResult *result = [context executeRequest:update error:&error];
    if (error == nil) {
        // 更新成功, 获取IDs
        NSArray *IDs = (NSArray*)result.result;
        // 告诉context数据已更新(同步)
        [NSManagedObjectContext mergeChangesFromRemoteContextSave:@{NSUpdatedObjectsKey: IDs} intoContexts:@[context]];
    } else {
        NSLog(@"更新失败: %@",error);
    }

批量删: NSBatchDeleteRequest

NSBatchDeleteRequest 是在iOS 9之后才引入的API, 使用和NSBatchUpdateRequest类似, 初始化方式稍有区别:

//获取代理
    AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    //获取context
    NSManagedObjectContext *context = [delegate managedObjectContext];
    // 创建查询请求
    NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"PeopleEntity"];
    // 设置查询条件(删除age为30的)
    request.predicate = [NSPredicate predicateWithFormat:@"age == 30"];
    // 创建NSBatchDeleteRequest
    NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc]initWithFetchRequest:request];
    // 设置返回值为IDs
    delete.resultType = NSBatchDeleteResultTypeObjectIDs;
    
    NSError *error;
    NSBatchDeleteResult *result = [context executeRequest:delete error:&error];
    
    if (error == nil) {
        // 删除成功
        NSArray *IDs = (NSArray*)result.result;
        // 告诉context数据已更新(同步)
        [NSManagedObjectContext mergeChangesFromRemoteContextSave:@{NSDeletedObjectsKey: IDs} intoContexts:@[context]];
    } else {
        // 删除失败
        NSLog(@"更新失败: %@",error);
    }

以上便是Core Data的一些增删改查的操作.

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

推荐阅读更多精彩内容