2017-05-06 16 views
1

は、それがバックグラウンドマルチスレッドの問題に対処するためにSwift 3では、コアデータの並行性がマルチスレッドでどのように機能しますか?私のプログラムで

DispatchQueue.global(qos: .background) 

self.concurrentQueue.sync(flags: .barrier) 

の両方を使用しています。私もその後

問題が発生するデバッグする-com.apple.CoreData.ConcurrencyDebug 1を有効

lazy var context: NSManagedObjectContext = { 
    return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.newBackgroundContext() 
}() 

3はので、私はchildContextを取得するには、最新の方法を使用して迅速である

1、がありますAPIコールとコールバックブロック(バックグラウンドスレッド)で、私はコアデータをフェッチし、編集してから保存する必要があります。上記のコードからself.contextを使ってperformBlockAndWaitを呼び出して、このブロックの内部にsaveを書き込もうとしました。プロセス全体は正常ですが、このブロックの外側でコールバックブロックの内部にアクセスしようとすると、エラーが発生します。私もobjectIdとgetObjectByIdをself.contextとself.context.parentの両方で取得しようとしましたが、この行でエラーが発生します。私は何が間違っていたのですか?これをどうすればいいですか?なぜなら私は多くの異なるスレッド(コンテキストではない)で結果をどこでも使う必要があるからです。

2、投稿ごとにスレッドごとに1つのコンテキストが必要だと言っていますが、私の場合、APIコールからのコールバックであれば、どのスレッドを正確に判断するのですか?

3私のプログラムはバックグラウンドスレッドで実行する必要があるため、このようにしなければならないので、なぜprivateConcurrentTypeが必要なのかを尋ねるかもしれません(他の投稿から読んでください)。

4私の質問でも、objectIdを別のContextに渡してオブジェクトを取得しても、私の場合は動作しません。これが正しい方法だとしましょう。どのように私は非常に乱雑ではなく、別のスレッドで私のプログラム全体を通して非常に多くのobjectIDを渡すことを管理するつもりですか?私にとっては、これは狂ったように聞こえるが、これに対処するためにもっとクリーンで簡単な方法があると思う。

5、いくつかの記事はかなり古くなっています(スワイプ3より前)childContext.saveparentContext.saveがありますが、私は上記のコードを使用しています(swift 3のみ)。私はそれが動作するようにchildContext.saveを行うことができるようですか?私は正しい?

+0

バックグラウンドスレッドのコンテキストにアクセスしようとすると、「エラー」とは何かが発生しますか? –

答えて

3

コアデータは一般的にマルチスレッド対応ではありません。並行スレッドで使用するには、悪いことしか起こらないと思います。コンテキストが存在するスレッド外の管理対象オブジェクトを操作するだけでは不十分です。

すでに言及したように、スレッドごとに別々のコンテキストが必要になりますが、私の経験では、読み書き可能なバックグラウンドコンテキストとフェッチに使用される単一のメインスレッドの読み取り専用コンテキストのみが必要です結果コントローラまたは他のインスタントフェッチ。

コンテキストは、データベース(ファイル)と通信するいくつかのメモリ内モジュールと考えることができます。フェッチされたエンティティはコンテキスト内で共有されますが、コンテキスト間では共有されません。したがって、コンテキスト内のほとんどすべてを変更できますが、コンテキストをデータベースに保存するまで、データベースやその他のコンテキストには表示されません。また、2つのコンテキストで同じエンティティを変更して保存した場合、競合が発生して解決されるはずです。

これらはすべてコードロジックでかなり混乱します。したがって、複数のコンテキストは避けるべきもののようです。私がしていることは、バックグラウンドコンテキストを作成し、そのコンテキストですべての操作を行うことです。 Contextには、メインコンテキスト(バックグラウンドコンテキスト用)ではなく、このスレッドがシリアルである独自のスレッドでコードを実行するメソッドperformがあります。

たとえば、スマートクライアントを実行しているときに、新しいエントリを持つサーバーからの応答が返されます。これらは即座に解析され、コンテキスト内でブロックを実行して、データベース内のすべての対応するオブジェクトを取得し、存在しないオブジェクトを作成します。その後、データをコピーし、コンテキストをデータベースに保存します。

UI部分についても同様です。エントリを保存すると、バックグラウンドコンテキストスレッド上でエンティティを作成または更新します。

public func performBlockOnBackgroundContextAndReturnOnMain(block: @escaping (() -> Void), main: @escaping (() -> Void)) { 
     if let context = context { 
      context.perform { 
       block() 
       DispatchQueue.main.async(execute: {() -> Void in 
        main() 
       }) 
      } 
     } 
    } 

だから、ほとんどすべてのコアデータ・ロジックのが背景にある単一のスレッドで起こる:私はメソッドを持っているので、その後、通常終了時に、いくつかのUIのものを行います。いくつかのケースでは、たとえば、メインコンテキストを使用して結果のコントローラを取得します。オブジェクトのリストを表示し、ユーザーがアイテムの1つを選択すると、そのアイテムをバックグラウンドコンテキストから再取得し、そのアイテムをユーザーインターフェイスで使用して変更します。

しかし、一部のプロパティがデータベースから遅延読み込みされる可能性があるので、必要なデータがすべてコンテキストにロードされ、メインスレッドでアクセスできるようにする必要があります。そのための方法がありますが、私はむしろラッパーを使用します:idを含むデータベースモデルのすべてのエンティティに対して1つのスーパークラスを持っています:

したがって、私はスーパークラスのラッパーも持っています。スーパークラスのラッパーには、残りのラッパーと一緒に動作するすべてのロジックがあります。私が最後に残していることは、それぞれのサブクラスに対して、2つのマッピングメソッド(管理対象オブジェクト)をオーバーライドする必要があることです。

追加のラッパーを作成し、管理オブジェクトからデータをメモリにコピーするのは馬鹿に思えるかもしれませんが、管理対象オブジェクトの大半は管理対象オブジェクトのために行う必要があります。 NSDataUIImageからNSDateからDateに変換すると、整数または文字列との間で列挙されるので、最後に1対1にコピーされた文字列を多かれ少なかれ残しています。また、これにより、このクラスのサーバーからの応答をマッピングするコードや、管理オブジェクトとの名前の競合がない追加のロジックを簡単に作成できます。

関連する問題