Dav*_*vid 10 sqlite core-data ios
之前已经提出过这个问题,但没有描述的解决方案足以满足我的应用需求.
在我们设置的通信协议中,服务器在每次执行同步时发送一组新的客户.早些时候,我们一直作为一个plist存储.现在想要使用Core Data.
可能有数千个条目.单独删除每一个需要很长时间.有没有办法删除Core Data中特定表中的所有行?
delete from customer
Run Code Online (Sandbox Code Playgroud)
sqlite中的这个调用立即发生.在Core1中单独浏览每一个可能需要30秒才能在iPad1上完成.
关闭Core Data是合理的,即删除持久性存储和所有托管对象上下文,然后放入sqlite并对表执行delete命令?在此过程中没有其他活动正在进行,因此我不需要访问数据库的其他部分.
Jod*_*ins 25
Dave DeLong是一位专家,几乎所有的东西,所以我觉得我告诉耶稣如何在水上行走.当然,他的帖子是从2009年开始的,这是很久以前的事了.
但是,Bot发布的链接中的方法不一定是处理大型删除的最佳方法.
基本上,该帖子建议获取对象ID,然后遍历它们,在每个对象上调用delete.
问题是,当您删除单个对象时,它必须处理所有关联的关系,这可能会导致进一步的提取.
因此,如果您必须执行此类大规模删除操作,我建议您调整整个数据库,以便隔离特定核心数据存储中的表.这样你就可以删除整个商店,并可能重建你想要保留的小位.这可能是最快的方法.
但是,如果要删除对象本身,则应遵循此模式...
在自动释放池中批量删除,并确保预先获取任何级联关系.所有这些一起将最大限度地减少您实际进入数据库的次数,从而减少执行删除所需的时间.
在建议的方法,归结为......
如果您有级联关系,那么您将遇到大量额外的数据库访问,并且IO非常慢.您希望最小化访问数据库的次数.
虽然它最初可能听起来违反直觉,但您希望获取的数据超出您想要删除的数据.原因是所有数据都可以在几个IO操作中从数据库中获取.
因此,在您的获取请求中,您要设置...
[fetchRequest setRelationshipKeyPathsForPrefetching:@[@"relationship1", @"relationship2", .... , @"relationship3"]];
Run Code Online (Sandbox Code Playgroud)
这些关系代表可能具有级联删除规则的所有关系.
现在,当您的提取完成后,您将拥有将要删除的所有对象,以及由于这些对象被删除而将被删除的对象.
如果您有一个复杂的层次结构,则希望尽可能提前预取.否则,当您删除对象时,Core Data将必须为每个对象单独获取每个关系,以便它可以管理级联删除.
这将浪费大量的时间,因为您将进行更多的IO操作.
现在,在您的提取完成后,您将遍历对象并删除它们.对于大型删除,您可以看到一个数量级的加速.
此外,如果您有很多对象,请将其分解为多个批次,并在自动释放池中执行.
最后,在单独的后台线程中执行此操作,因此您的UI不会挂起.您可以使用连接到持久性存储协调器的单独MOC,并让主MOC处理DidSave通知以从其上下文中删除对象.
这看起来像代码,将其视为伪代码...
NSManagedObjectContext *deleteContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateConcurrencyType];
// Get a new PSC for the same store
deleteContext.persistentStoreCoordinator = getInstanceOfPersistentStoreCoordinator();
// Each call to performBlock executes in its own autoreleasepool, so we don't
// need to explicitly use one if each chunk is done in a separate performBlock
__block void (^block)(void) = ^{
NSFetchRequest *fetchRequest = //
// Only fetch the number of objects to delete this iteration
fetchRequest.fetchLimit = NUM_ENTITIES_TO_DELETE_AT_ONCE;
// Prefetch all the relationships
fetchRequest.relationshipKeyPathsForPrefetching = prefetchRelationships;
// Don't need all the properties
fetchRequest.includesPropertyValues = NO;
NSArray *results = [deleteContext executeFetchRequest:fetchRequest error:&error];
if (results.count == 0) {
// Didn't get any objects for this fetch
if (nil == results) {
// Handle error
}
return;
}
for (MyEntity *entity in results) {
[deleteContext deleteObject:entity];
}
[deleteContext save:&error];
[deleteContext reset];
// Keep deleting objects until they are all gone
[deleteContext performBlock:block];
};
[deleteContext preformBlock:block];
Run Code Online (Sandbox Code Playgroud)
当然,您需要进行适当的错误处理,但这是基本的想法.
如果您有太多要删除的数据,则会批量获取,这会使内存瘫痪.不要获取所有属性.预取关系以最小化IO操作.使用autoreleasepool可以防止内存增长.修剪上下文.在后台线程上执行任务.
如果您有一个非常复杂的图形,请确保预取整个对象图中所有实体的所有级联关系.
请注意,您的主要上下文必须处理DidSave通知,以使其上下文与删除保持同步.
编辑
谢谢.很多好点.除了为什么创建单独的MOC之外,所有解释都很好?有没有删除整个数据库,但使用sqlite删除特定表中的所有行的想法?- 大卫
您使用单独的MOC,因此在执行长删除操作时不会阻止UI.请注意,当实际提交到数据库时,只有一个线程可以访问数据库,因此任何其他访问(如提取)都将阻止任何更新.这是将大型删除操作分解为块的另一个原因.小件工作将为其他MOC提供访问商店的机会,而无需等待整个操作完成.
如果这会导致问题,您还可以实现优先级队列(via dispatch_set_target_queue),但这超出了此问题的范围.
至于在Core Data数据库上使用sqlite命令,Apple一再表示这是一个坏主意,你不应该在Core Data数据库文件上运行直接SQL命令.
最后,让我注意一下.根据我的经验,我发现当我遇到严重的性能问题时,通常是设计不良或实施不当造成的.重新审视您的问题,看看您是否可以稍微重新设计系统以更好地适应此用例.
如果必须发送所有数据,可能在后台线程中查询数据库并过滤新数据,以便将数据分成三组:需要修改的对象,需要删除的对象和需要插入的对象.
这样,您只需要更改需要更改的数据库.
如果数据每次都是全新的,请考虑重构数据库,这些实体拥有自己的数据库(我假设您的数据库已包含多个实体).这样你就可以删除文件,并从一个新的数据库重新开始.那很快.现在,重新插入数千个对象并不会很快.
您必须跨商店手动管理任何关系.这并不困难,但它不像同一商店内的关系那样自动化.
如果我这样做,我将首先创建新数据库,然后拆除现有数据库,将其替换为新数据库,然后删除旧数据库.
如果您只是通过此批处理机制操作数据库,并且不需要对象图管理,那么您可能要考虑使用sqlite而不是Core Data.
使用NSBatchDeleteRequest.我在Core Data实体上的模拟器中测试了这个,它有超过400,000个实例,删除几乎是瞬间的.
// fetch all items in entity and request to delete them
let fetchRequest = NSFetchRequest(entityName: "MyEntity")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
// delegate objects
let myManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let myPersistentStoreCoordinator = (UIApplication.sharedApplication().delegate as! AppDelegate).persistentStoreCoordinator
// perform the delete
do {
try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myManagedObjectContext)
} catch let error as NSError {
print(error)
}
Run Code Online (Sandbox Code Playgroud)
请注意,@ Boot关联的答案和@JodyHagins提到的答案也已更新为此方法.
| 归档时间: |
|
| 查看次数: |
21716 次 |
| 最近记录: |