2015-12-23 102 views
1

我的应用程序在运行时可能会创建/删除数千个托管对象。我使用NSPrivateQueueConcurrencyTypeNSOperation秒的次要NSManagedObjectContext s(MOC),以使应用程序更具响应能力,并且大多数零件都能正常工作。但是当我按下⌘Q并且未保存的对象数量很大时,应用程序会在窗户关闭之前挂起相当长的一段时间(沙滩球继续旋转......)。CoreData应用程序需要太长时间才能退出

如何使窗口立即消失,之前 MOC的保存? 我试图在AppDelegate中插入window.close(),applicationShouldTerminate,但它没有效果。

我的代码删除没有什么特别的,除了层次真的很大。类似于

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 practicehttps://www.cocoanetics.com/2012/07/multi-context-coredata/

首先,我做了一个backgroundMOC.PrivateQueueConcurrencyType

lazy var backgroundMOC : NSManagedObjectContext = { 
    let coordinator = self.persistentStoreCoordinator 
    let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType) 
    moc.persistentStoreCoordinator = coordinator 
    moc.undoManager = nil 
    return moc 
}() 

然后使它原来商务部Prent公司。

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

+1

调用'[[NSRunningApplication currentApplication] hide];',从'applicationShouldTerminate'返回'NSTerminateLater'并让应用程序完成它应该完成的任务。然后调用'replyToApplicationShouldTerminate:YES' – bteapot

+0

因此,我应该在CoreApp MOC中保存并在'applicationShouldTerminate:'启动的'NSOperation'中调用'replyToApplicationShouldTerminate:YES',然后返回NSTerminateLater? – LShi

+0

否,序列为:1) 。用户命令应用程序退出。 2)。应用程序委托收到'applicationShouldTerminate:'调用。 3)。在这种方法中你必须a)。 b)在后台线程中启动保存进程。返回'NSTerminateLater'。 4)。当保存过程完成后,您应该在主线程上调用'replyToApplicationShouldTerminate:YES'。 – bteapot

回答

0

退出应用程序可能需要一段时间,因为它将首先清空队列中的进程。 您是否要立即放弃舍弃父母或孩子MOC中的所有内容?但是这会导致数据丢失。

如果您有多窗口应用程序,那么只关闭窗口但不能退出应用程序。

如果您正确地管理了它,成千上万的条目不应超过5秒才能得到处理和保存。您的代码中可能存在一些漏洞,请尝试优化使用Instruments,CoreData分析器工具,以帮助您了解它耗尽的时间。

要隐藏窗口,您可以使用下面的窗口,在后台进行所有的coredata处理,一旦完成所有操作,应用程序将终止。

[self.window orderOut:nil]; 
+0

不应丢弃数据更改。即使我在'moc.save()'之前添加'windows.close()',窗口仍然会在那里,直到保存结束。 – LShi

+0

如果您可以显示代码的写法和保存方式,那就太好了。也可以尝试'CoreData Profiler'。 –

+0

我使用Xcode生成'applicationShouldTerminate:'在'AppDelegate'中进行保存。我认为所花费的时间是合理的。我想立即隐藏窗口,感觉很快。 – LShi

相关问题