2017-04-19 26 views
1

CoreDataでデータベースを取得する関数を作成しました。この関数はクロージャを実行し、performBackgroundTaskを実行してデータをフェッチします。次に、結果をクロージャに渡して実行します。私はcontextを交換する場合CoreDataのperformBackgroundTaskメソッドがクラッシュする

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) 
      } 
     } 
    } 
} 

しかし:

@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を使用して墜落した私が書いた機能(ない方法)である:私は簡単にviewContextにアクセスするため 私はAppDelegateに静的プロパティを書きましたAppDelegate.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

は、ここではいくつかの問題です:context.performAndWaitを呼び出すと、デッドロックやクラッシュにつながる可能性が理由がないので、

  • performBackgroundTaskは、コンテキストのための右のスレッドですでにあります。
  • performBackgroundTaskでフェッチまたは作成されたアイテムは、いかなる場合でもブロックを残すことはできません。コンテキストがブロック終了時に破棄され、コンテキストにアクセスしようとするとmanagedObjectsがクラッシュする
  • コアデータスレッドの安全性を管理するのが難しくなる可能性があります。そのオブジェクトのコンテキストが明示的かつ明確でない限り、オブジェクトを関数に変換します。これは壊れないルールではありませんが、私はそれがあなたのapisを作るときの経験則であると思います。
  • performBackgroundTaskは、通常、コアデータの更新に使用されます。フェッチのみを行う場合は、viewContextを使用する必要があります。バックグラウンドでフェッチを行うだけで、メインスレッドに渡すのは一般的には無駄です。
  • performBackgroundTaskブロックでは、viewContextにアクセスすることはできません。読み込みと書き込みのどちらもできません。もしあなたがそうするならば、アプリケーションはスレッドセーフティに違反していないときでさえ、クラッシュレポートを混乱させて、いつでもクラッシュする可能性があります。
  • 作成している述語がどのように見えるのか分かりませんが、間違っていると感じています。これにより、フェッチ時にクラッシュが発生します。

全体的に私が作成した機能には価値がないと思います。フェッチを行うだけであれば、述部とソート記述子を作成してviewContextでフェッチするだけです。関数の保持を主張する場合は、performBackgroundTaskを削除し、viewContextを使用してフェッチし、(コールバックではなく)結果を返し、メインスレッドからのみコールします。

+0

条件は正常です。 Xcodeが示すように、viewContextだけがクラッシュします。アプリのサイズが大きくなるにつれ、私はperformbacktaskを削除しました。私は常にUIのスタッフのメインキューに渡す必要があります。コンテキストを使用すると、performbacktaskが提供するコンテキストがクラッシュするという点が分かりません。 –

+0

performBackgroundTaskによって提供されたコンテキストでのフェッチはクラッシュしません。何か間違っているので、コード内でクラッシュします。コードには多くの問題があり、クラッシュレポートを見たことがないので、クラッシュの原因を正確に突き止めるのは難しいです。しかし、私の最高のゲストは、実際のクラッシュはコンテキストが処理された後にバックグラウンドコンテキストのmanagedObjectsにアクセスすることによるものです(上記のリストの2番目の項目)。 –

関連する問題