2017-04-19 96 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(上述第二項)。 –