增:
新添加一条数据,首先需要创建被添加的数据,使用 NSEntityDesctiption 类的 + (__kindof NSManagedObject *)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context; 方法来创建一个新的 NSManagedObject 对象,参数分别是 entityName 和 managedObjectContext,entityName 也就是实体类的名字,例如,我要插入一条新的 Student 字段,entityName 就是 @”Student”;
context 是 NSManagedObjectContext对象,新增的实体类对象会添加到对应的 context 上下文对象中。这个方法返回的是一个 NSManagedObject 实例,可以根据具体情况转换成相应的子类
Student *stu = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
stu.name = @"kobe";
stu.age = 20;
NSError *error;
[self.context save:&error];
//如果循环添加多条数据时,将数据都添加完之后再调用[self.context save:&error]方法
调用 save 方法时,可以传入一个 NSError 的指针,如果数据保存出错的话,错误信息会保存到 error 里,这也是 Objective-C 里通常处理错误的方式
查:
从数据库中查询数据,会用到三个类:NSFetchRequest
,NSPredicate
,NSSortDescriptor
,分别说一下这三个类的作用:
- NSFetchRequest — fetchRequest 代表了一条查询请求,相当于 SQL 中的 SELECT 语句
- NSPredicate — predicate 翻译过来是谓词的意思,它可以指定一些查询条件,相当于 SQL 中的 WHERE 子句
- NSSortDescriptor — sortDescriptor 是用来指定排序规则的,相当于 SQL 中的 ORDER BY 子句
在NSFetchRequest
中有两个属性 predicate
、sortDescriptors
,就是用来指定查询的限制条件的。其中 sortDescriptors
是一个 NSSortDescriptor
的数组,也就是可以给一个查询指定多个排序规则,这些排序规则的优先级就是它们在数组中的位置,数组前面的优先级会比后面的高。除此之外,NSFetchRequest
还有下面这些属性
- fetchLimit — 指定结果集中数据的最大条目数,相当于 SQL 中的 LIMIT 子句
- fetchOffset — 指定查询的偏移量,默认为 0
- fetchBatchSize — 指定批处理查询的大小,设置了这个属性后,查询的结果集会分批返回
- entityName/entity — 指定查询的数据表,相当于 SQL 中的 FROM 语句
- propertiesToGroupBy — 指定分组规则,相当于 SQL 中的 GROUP BY 子句
- propertiesToFetch — 指定要查询的字段,默认会查询全部字段
配置好 NSFetchRequest
对象后,需要调用 NSManagedObjectContext
的 - (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
来执行查询,返回的数组就是查询出的结果集
//一条查询请求,相当于 SQL 中的 SELECT 语句
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//实体
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:self.context];
[fetchRequest setEntity:entityDescription];
//还可以写成 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; 或 NSFetchRequest *fetchRequest = [Student fetchRequest];
//查询条件
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"age > %@", @(20)];
//排序
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSError *error;
NSArray <Student *> *fetchObjects = [self.context executeFetchRequest:fetchRequest error:&error];
if (!fetchObjects.count) {
NSLog(@"没有数据");
}else {
NSLog(@"%@",fetchObjects);
}
删:
将上一步查询到的结果,调用 NSManagedObjectContext 的 - (void)deleteObject:(NSManagedObject *)object; 方法来删除一个条目。例如,将上面查询出来的 students 全部删除,可以这么写:
for (Student *stu in fetchObjects) {
[self.context deleteObject:stu];
}
NSError *error;
[self.context save:&error];
//NSBatchDeleteRequest 批量删除
// NSBatchDeleteRequest 的用法和 NSBatchUpdateRequest 很相似,不同的是 NSBatchDeleteRequest 需要指定 fetchRequest 属性来进行删除;而且它是 iOS 9 才添加进来的,和 NSBatchUpdateRequest 的适用范围不一样
NSFetchRequest *deleteFetch = [Student fetchRequest];
deleteFetch.predicate = [NSPredicate predicateWithFormat:@"age == %@", @(20)];
NSBatchDeleteRequest *deleteRequest = [[NSBatchDeleteRequest alloc] initWithFetchRequest:deleteFetch];
deleteRequest.resultType = NSBatchDeleteResultTypeObjectIDs;
NSBatchDeleteResult *deleteResult = [self.context executeRequest:deleteRequest error:nil];
NSArray<NSManagedObjectID *> *deletedObjectIDs = deleteResult.result;
NSDictionary *deletedDict = @{NSDeletedObjectsKey : deletedObjectIDs};
[NSManagedObjectContext mergeChangesFromRemoteContextSave:deletedDict intoContexts:@[self.context]];
改:
将之前查询的到的数据进行如下操作可进行修改:
for (Student *student in fetchObjects) {
student.name = @"newName";
}
[self.context save:nil];
//批量更新,将所有name更改为Tom,用到的是集合类型中的 KVC 特性
[students setValue:@"Tom" forKeyPath:@"name"];
//NSBatchUpdateRequest 批量更新
// NSBatchUpdateRequest 是在 iOS 8, macOS 10.10 之后新添加的 API,它是专门用来进行批量更新的。因为用上面那种方式批量更新的话,会存在一个问题,就是更新前需要将要更新的数据,查询出来,加载到内存中;这在数据量非常大的时候,假如说要更新十万条数据,就比较麻烦了,因为对于手机这种内存比较小的设备,直接加载这么多数据到内存里显然是不可能的。解决办法就是每次只查询出读取一小部分数据到内存中,然后对其进行更新,更新完之后,再更新下一批,就这样分批来处理。但这显然不是高效的解决方案。
//
// 于是就有了 NSBatchUpdateRequest 这个 API。它的工作原理是不加载到内存里,而是直接对本地数据库中数据进行更新。这就避免了内存不足的问题;但同时,由于是直接更新数据库,所以内存中的 NSManagedObjectContext 不会知道数据库的变化,解决办法是调用 NSManagedObjectContext 的 + (void)mergeChangesFromRemoteContextSave:(NSDictionary*)changeNotificationData intoContexts:(NSArray<NSManagedObjectContext*> *)contexts;方法来告诉 context,有哪些数据更新了。
// 根据 entity 创建
NSBatchUpdateRequest *updateRequest = [[NSBatchUpdateRequest alloc] initWithEntity:[Student entity]];
// 根据 entityName 创建
// NSBatchUpdateRequest *updateRequest = [[NSBatchUpdateRequest alloc] initWithEntityName:@"Student"];
updateRequest.predicate = [NSPredicate predicateWithFormat:@"studentAge == %@", @(20)];
// propertiesToUpdate 属性是一个字典,用它来指定需要更新的字段,字典里的 key 就是要更新的字段名,value 就是要设置的新值。因为 Objective-C 字典里只能存储对象类型,所以如果字段基本数据类型的的话,需要转换成 NSNumber 对象
updateRequest.propertiesToUpdate = @{@"name" : @"anotherName"};
updateRequest.resultType = NSUpdatedObjectIDsResultType;
NSBatchUpdateResult *updateResult = [self.context executeRequest:updateRequest error:nil];
NSArray<NSManagedObjectID *> *objectIDs = updateResult.result;
NSLog(@"ids===%@",objectIDs);
网友评论