2017-04-19 92 views
1

我写了一个函数来获取CoreData中的数据库。这个函数将关闭并运行performBackgroundTask来获取数据。然后,将结果传递给闭包运行。 我AppDelegate写静态属性,我可以方便地访问viewContextCoreData的performBackgroundTask方法崩溃

@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate { 

    var window: UIWindow? 
    static var persistentContainer: NSPersistentContainer { 
     return (UIApplication.shared.delegate as! AppDelegate).persistentContainer 
    } 

    static var viewContext: NSManagedObjectContext { 
     return persistentContainer.viewContext 
    } 
    // ... 
} 

以下是我写的函数(不是法)坠毁利用context

func fetch<T>(fetchRequest: NSFetchRequest<T>, keyForOrder: String? = nil, format: String? = nil, keyword: String? = nil, handler: (([T]?)->Void)? = nil) where T:NSManagedObject, T: NSFetchRequestResult { 
    AppDelegate.persistentContainer.performBackgroundTask{(context: NSManagedObjectContext) in 
     if let format = format?.trimmingCharacters(in: .whitespacesAndNewlines), 
      !format.isEmpty, 
      let keyword = keyword?.trimmingCharacters(in: .whitespacesAndNewlines), 
      !keyword.isEmpty { 
      fetchRequest.predicate = NSPredicate(format: format, keyword) 
     } 

     if let keyForOrder = keyForOrder { 
      fetchRequest.sortDescriptors = [NSSortDescriptor(key: keyForOrder, ascending: true)] 
     } 

     guard let cats = try? context.fetch(fetchRequest) else { // crash 
      return 
     } 

     context.performAndWait(){ // crash 
      if let handler = handler { 
       handler(cats) 
      } 
     } 
    } 
} 

,但如果我更换contextAppDelegate.viewContext,功能不会崩溃:

func fetch<T>(fetchRequest: NSFetchRequest<T>, keyForOrder: String? = nil, format: String? = nil, keyword: String? = nil, handler: (([T]?)->Void)? = nil) where T:NSManagedObject, T: NSFetchRequestResult { 
    AppDelegate.persistentContainer.performBackgroundTask{(context: NSManagedObjectContext) in 
     if let format = format?.trimmingCharacters(in: .whitespacesAndNewlines), 
      !format.isEmpty, 
      let keyword = keyword?.trimmingCharacters(in: .whitespacesAndNewlines), 
      !keyword.isEmpty { 
      fetchRequest.predicate = NSPredicate(format: format, keyword) 
     } 

     if let keyForOrder = keyForOrder { 
      fetchRequest.sortDescriptors = [NSSortDescriptor(key: keyForOrder, ascending: true)] 
     } 

     guard let cats = try? AppDelegate.viewContext.fetch(fetchRequest) else { // crash 
      return 
     } 

     AppDelegate.viewContext.performAndWait(){ // crash 
      if let handler = handler { 
       handler(cats) 
      } 
     } 
    } 
} 

什么正在发生?

谢谢。

回答

0

这里有一些问题:

  • performBackgroundTask已经是上下文正确的线程上,所以没有理由骂context.performAndWait,并可能导致死锁或崩溃。
  • 在performBackgroundTask中获取或创建的项目在任何情况下都不能离开该块。上下文将在块的末尾销毁,并且managedObjects在尝试访问其上下文时将崩溃
  • 管理核心数据线程的安全性可能很困难,我发现通常不会传递或返回managed对象的功能,除非对象的上下文是明确和清晰的。这不是一个牢不可破的规则,但我认为制定你的API是一个很好的经验法则。
  • performBackgroundTask通常用于更新核心数据。如果你只是在提取你应该使用viewContext。在后台执行提取操作只会将其传递到主线程通常是浪费。
  • 在performBackgroundTask块中,您无法访问viewContext - 既不能读取也不能写入。如果你这样做的话,应用程序可能会随时崩溃,甚至会在以后没有违反线程安全的情况下发生崩溃报告。
  • 我不知道你创造的谓词是什么样的,但我有强烈的感觉,他们错了。这会在读取时导致崩溃。

总的来说我认为你创建的函数没什么价值。如果它所做的只是一个提取,那么你应该简单地创建谓词和排序描述符并在viewContext上进行提取。如果你坚持保留这个函数,那么删除performBackgroundTask,使用viewContext获取,返回结果(而不是回调),并且只能从主线程调用它。

+0

谓词没问题。只有viewcode冲突Xcode显示。随着应用程序变得越来越大,我删除了performbacktask,我总是需要传递给UI员工的主队列。我只是不明白,如果我使用上下文将会导致performbacktask提供的崩溃。 –

+0

对performBackgroundTask提供的上下文的提取不会崩溃。它在你的代码中崩溃,因为你做错了什么。代码有很多错误,并且我没有看到崩溃报告,所以很难确定究竟是什么导致了崩溃。但是,我最好的客户将是实际的崩溃,这是因为在上下文关闭之后访问后台上下文的managedObjects(上述第二项)。 –