美文网首页iOS开发知识收录
[iOS]Core Data浅析三 -- 数据的增删改查

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

作者: 流火绯瞳 | 来源:发表于2017-07-01 14:23 被阅读340次

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的一些增删改查的操作.

相关文章

网友评论

  • 6268e6229a8b:sql里面可以设置主键,这个在core data里面怎么处理?比如在sql里面我以用户id为主键建的表,当我加一个用户时,我不知道表里面有没有,我设置主键后,如果有这个用户id的数据,则覆盖表里面的数据,如果没有,则插入一条数据
    流火绯瞳:@ChengLiH 给模型设置一个值唯一的属性就行,增删改查都已这个属性的值为依据

本文标题:[iOS]Core Data浅析三 -- 数据的增删改查

本文链接:https://www.haomeiwen.com/subject/gljmcxtx.html