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
方法的时候, 才是真正将数据插入到数据库中;
查
因为一般的删改的前提是需要查询出来, 所以先讲如何查;
从数据库中查询数据,会用到三个类:NSFetchRequest
,NSPredicate
,NSSortDescriptor
- 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的一些增删改查的操作.