【发布时间】:2016-03-29 19:37:26
【问题描述】:
我的应用在运行时可能会创建/删除数千个托管对象。我使用辅助 NSManagedObjectContexts(MOCs) 和 NSPrivateQueueConcurrencyType 和 NSOperations 来使应用程序更具响应性,并且大多数部分运行良好。但是当我按下 ⌘Q 并且如果未保存的对象数量很大时,应用程序会在窗口关闭之前挂起很长一段时间(沙滩球继续旋转......)。
如何让窗口立即消失,在 MOC 保存之前?
我尝试在AppDelegate中的applicationShouldTerminate中插入window.close(),但是没有效果。
我的删除代码没什么特别的,只是层次结构非常大。类似的东西
let items = self.items as! Set<Item>
Group.removeItems(items)
for i in items {
self.managedObjectContext?.deleteObject(i)
}
Item 是一个分层实体。 Group 与项目具有一对多的关系。
removeItems 由 CoreData 使用 @NSManaged 生成。
非常感谢。
更新
我尝试了以下代码,保存仍然会阻塞 UI。
@IBAction func quit(sender: AnyObject) {
NSRunningApplication.currentApplication().hide()
NSApp.terminate(sender)
}
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply
{
let op = NSBlockOperation { () -> Void in
do {
try self.managedObjectContext.save()
} catch {
print("error")
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
NSApp.replyToApplicationShouldTerminate(true)
})
}
op.start()
return .TerminateLater
}
当创建/删除的托管对象数量很大时,这不会使窗口首先关闭。
然后按照@bteapot 的建议,我更改为以下内容。仍然没有效果。窗口仍然不会立即关闭。
@IBAction func quit(sender: AnyObject) {
NSRunningApplication.currentApplication().hide()
NSApp.terminate(sender)
}
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
let op = NSBlockOperation { () -> Void in
self.managedObjectContext.performBlock({ () -> Void in
do {
try self.managedObjectContext.save()
} catch {
print("errr")
}
})
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
NSApp.replyToApplicationShouldTerminate(true)
})
}
dispatch_async ( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
{() -> Void in
op.start()
})
return .TerminateLater
}
最后我解决了这个问题,虽然 UI有时仍然被阻塞,即使使用相同的测试数据。
使用的方法可以在这里找到:https://blog.codecentric.de/en/2014/11/concurrency-coredata/、Core Data background context best practice、https://www.cocoanetics.com/2012/07/multi-context-coredata/
首先我用.PrivateQueueConcurrencyType做了一个backgroundMOC
lazy var backgroundMOC : NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
moc.undoManager = nil
return moc
}()
然后把它变成原来的 moc。
lazy var managedObjectContext: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
// managedObjectContext.persistentStoreCoordinator = coordinator
managedObjectContext.parentContext = self.backgroundMOC
managedObjectContext.undoManager = nil
return managedObjectContext
}()
两种保存方法。
func saveBackgroundMOC() {
self.backgroundMOC.performBlock { () -> Void in
do {
try self.backgroundMOC.save()
NSApp.replyToApplicationShouldTerminate(true)
} catch {
print("save error: bg")
}
}
}
func saveMainMOC() {
self.managedObjectContext.performBlock { () -> Void in
do {
try self.managedObjectContext.save()
self.saveBackgroundMOC()
} catch {
print("save error")
}
}
}
将applicationShouldTerminate() 更改为
func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
if !managedObjectContext.commitEditing() {
NSLog("\(NSStringFromClass(self.dynamicType)) unable to commit editing to terminate")
return .TerminateCancel
}
if !managedObjectContext.hasChanges {
return .TerminateNow
}
saveMainMOC()
return .TerminateLater
}
这么慢的原因是我使用的是NSXMLStoreType 而不是NSSQLiteStoreType。
【问题讨论】:
-
调用
[[NSRunningApplication currentApplication] hide];,从applicationShouldTerminate:返回NSTerminateLater,然后让应用完成它应该做的事情。然后拨打replyToApplicationShouldTerminate:YES -
那么我应该在
applicationShouldTerminate:发起的NSOperation中保存CoreData MOC并调用replyToApplicationShouldTerminate:YES然后返回NSTerminateLater吗? -
不,顺序是:1)。用户命令应用程序退出。 2)。应用程序委托接收
applicationShouldTerminate:调用。 3)。在这种方法中,您必须 a)。在后台线程和 b) 中启动保存过程。返回NSTerminateLater。 4)。保存过程完成后,您应该在主线程上调用replyToApplicationShouldTerminate:YES。 -
还是没有运气。我在问题中添加了代码。
-
只需将
op.start()包装在dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ });中,将self.managedObjectContext.save()包装在self.managedObjectContext.performBlock中。
标签: objective-c swift macos cocoa core-data