NSBatchDeleteRequest 是 Core Data 提供的一种高效删除大量数据的机制,特别适合删除 Core Data 存储中成千上万条数据而不影响内存使用。
Core Data删除数据
在 Core Data 中,可以用以下方式删除所有数据:
let results = try context.fetch(fetchRequest)
for object in results {
context.delete(object)
}
这会把所有数据加载到内存中再删除,非常耗内存,对性能不好。
而使用 NSBatchDeleteRequest,Core Data 会在底层直接对数据库执行 SQL DELETE 操作,不加载任何对象到内存,更快更节省资源。
NSBatchDeleteRequest删除全部数据:
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = 实体名称.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try context.execute(deleteRequest)
context.reset() // 重置 context,释放内存中已删除的对象
} catch {
print("删除失败: \(error)")
}
例如,删除Yahoo黄金的图表数据:
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = YahooGoldPricePoint.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try context.execute(deleteRequest)
context.reset() // 重置 context,释放内存中已删除的对象
} catch {
print("删除失败: \(error)")
}
删除条件
如果需要设置删除条件(例如只删除符合某个条件的数据),只需要对 fetchRequest 添加 predicate(谓词)条件,和正常的 NSFetchRequest 一样。
例如,只删除updateTime 小于某个日期的 YahooGoldPricePoint 数据:
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = YahooGoldPricePoint.fetchRequest()
// 设定删除条件,比如只删除一个月前的数据
fetchRequest.predicate = NSPredicate(format: "updateTime < %@", someDate as NSDate)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try context.execute(deleteRequest)
context.reset() // 重置 context,防止引用已删除对象
} catch {
print("批量删除失败:\(error)")
}
fetchRequest 的类型要用 NSFetchRequest<NSFetchRequestResult>,不能是 NSFetchRequest<YahooGoldPricePoint>。
这是因为NSBatchDeleteRequest 处理的是低级别批量删除操作,它直接操作 SQLite,不加载对象进内存,它不认识具体的 YahooGoldPrice 泛型,因此必须写成:
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = YahooGoldPrice.fetchRequest()
注意事项
NSBatchDeleteRequest 默认不会更新 NSManagedObjectContext 中的缓存数据。
如果之后还用 @FetchRequest 或 NSFetchedResultsController,可能会出问题。
解决方法:设置 resultType 为 .resultTypeObjectIDs 并合并变化。
deleteRequest.resultType = .resultTypeObjectIDs
if let result = try context.execute(deleteRequest) as? NSBatchDeleteResult,
let objectIDs = result.result as? [NSManagedObjectID] {
let changes = [NSDeletedObjectsKey: objectIDs]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
}
当使用context.reset()时,会释放内存中的数据。如果配合插入操作,那么插入操作必须是否使用context.sace()保存。
try context.save()
否则,context.reset()会将未保存的插入数据一并清空。
总结
NSBatchDeleteRequest直接操作数据库,不加载对象到内存,删除效率高。适合批量删除历史记录、清理缓存等操作。
因为不会自动更新内存中的对象,因此需要手动合并或调用context.reset()。