问题描述
我正在开发一个允许用户对coredata进行批量更改的应用程序,并且我正在尝试确定解决此问题的最佳方法。
设置
我有两个实体,一个是(Main)记录,另一个是(secondary)记录,它们与(Main)记录有一对多的关系。 这由主/详细样式设置中的2个表视图表示
例如如果用户点击(View控制器A)上的(Main)表行,则会通过一对多关系显示(View控制器B)上表中的(Secondary)数据。 (最多可以有数千条记录)
在(视图控制器B)中,用户可以选择不删除,不删除部分或全部(辅助)记录
您可以想象,如果有几千条记录,则可能需要一段时间。
我知道可以使用“批量删除”选项,但是在这种情况下,我想使用标准的context.delete(record)方法
我想出的解决方案是有一个单例,它有2个主要dispatchQueue,1个是顺序的,另一个是并发的,因为一些大容量更改需要按它们初始化的顺序完成,而有些则不需要t
我还想将这些队列注册为长时间运行的后台任务,以便在将应用程序发送到后台时使它们有更多的时间运行
我要达到的目的是,如果用户删除几批记录,它将删除请求发送到单例,然后将其添加到队列中,一旦队列中的所有项目都完成,它将通知后台任务已完成的系统,因此现在可以安全地挂起该应用了。
这是我用于单例的代码:
class GlobalChanges {
static let shared = GlobalChanges()
// MARK: - Initialization
private init() {}
//MARK: - the global background queues that orgainise the background threads
//Queue that performs tasks one after the other
let serialQueue = dispatchQueue(label: "backgroundSerialQueue")
let serialGroup = dispatchGroup()
//the background task id of the serial queue
//used to register it as a long running background task
var serialBackgroundTaskId: uibackgroundtaskIdentifier? = .invalid
//Queue that performs tasks concurrently
let concurrentQueue = dispatchQueue(label: "backgroundConcurrentQueue",attributes: .concurrent)
let concurrentGroup = dispatchGroup()
//the background task id of the concurrent queue
//used to register it as a long running background task
var concurrentBackgroundTaskId: uibackgroundtaskIdentifier? = .invalid
//MARK: - Delete Records
func deleteRecords(records: [NSManagedobject],managedContext: NSManagedobjectContext,finished: @escaping () -> Void){
//create a new background MOC
let coreDataManager = CoreDataStack.shared
let backgroundContext = coreDataManager.persistentContainer.newBackgroundContext()
backgroundContext.automaticallyMergesChangesFromParent = true
//register the queue as a long running background task,if it's not already registered
if(self.concurrentBackgroundTaskId == .invalid){
self.concurrentBackgroundTaskId = UIApplication.shared.beginBackgroundTask(
withName: "concurrent background task",expirationHandler: {
//add the context save to the background save queue
coreDataManager.SaveBackgroundContext(backgroundContext: backgroundContext,mainContext: managedContext)
//end the background task
UIApplication.shared.endBackgroundTask(self.concurrentBackgroundTaskId!)
self.concurrentBackgroundTaskId = uibackgroundtaskIdentifier.invalid
})
}
//enter the group
concurrentGroup.enter()
//perform task asynchronously
concurrentQueue.async{
sleep(10)//<-- here just to help debug
backgroundContext.performAndWait {
//remove the records from the managed context
for record in records{
//Get the record by it's ID,so it can be manipulated safely
let backgroundContextRecord = backgroundContext.object(with: record.objectID) as NSManagedobject
//delete the record
backgroundContext.delete(backgroundContextRecord)
}
//add the context save to the background save queue
coreDataManager.SaveBackgroundContext(backgroundContext: backgroundContext,mainContext: managedContext)
//set operation has finished
finished()
print("Finished delete")
//leave te group
self.concurrentGroup.leave()
//If all items in the queue have finished
self.concurrentGroup.notify(queue: self.concurrentQueue) {
print("Task ended")
//end long running task
UIApplication.shared.endBackgroundTask(self.concurrentBackgroundTaskId!)
self.concurrentBackgroundTaskId = uibackgroundtaskIdentifier.invalid
}
}
}
}
}
Finished delete
Finished delete
Task ended
Task ended
2020-08-27 08:11:53.343126+0100 Burn[9643:3585892] Can't end BackgroundTask: no background task exists with identifier 3 (0x3),or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.
它按预期运行,并在队列中的所有项目完成后调用“任务已结束”,但似乎被调用了两次,这会导致尝试结束不存在的后台任务时出错
我将如何使其仅被调用一次?
我知道我可以添加一个检查以查看任务是否已经结束,如果这样做,不要尝试再次结束它,但是我希望仅在所有任务完成后才调用它。
此外,我还是Swift的新手,所以这个解决方案是个好主意,还是潜在的灾难等待发生?
任何帮助将不胜感激
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)