【Swift/Core Data】perform/performAndWaitの使い方!newBackgroundContext
この記事からわかること
- Swift/Core Dataでperform/performAndWaitメソッドの使い方
- newBackgroundContextを参照するときの注意点
- マルチスレッド対応
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献:公式リファレンス:Core Data プログラミングガイド〜Concurrency〜
環境
- Xcode:15.0.1
- iOS:17.0
- Swift:5.9
- macOS:Sonoma 14.1
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
newBackgroundContextとは?
func newBackgroundContext() -> NSManagedObjectContext
Core Dataでデータを操作する際に長時間かかる処理や大量のデータを処理する時などメインスレッドで実行したくない(UIをブロックしたくない)場合にはスレッドを分けて処理を実装することも多いと思います。しかしCore Dataはスレッドセーフではないためマルチスレッドでデータを扱う際には異なるスレッドから同一のContext(NSManagedObjectContext)を操作しないように注意が必要になります。Contextを異なるスレッドから参照しないようにする方法はいくつかあるようですがnewBackgroundContext
メソッドもその1つです。
/// メインスレッドで使用するContext
private func makeContext() -> NSManagedObjectContext {
let context = persistentContainer.viewContext
context.automaticallyMergesChangesFromParent = true
return context
}
/// バックグラウンドスレッドで使用するContext
private func makeBackgroundContext() -> NSManagedObjectContext {
let context = persistentContainer.newBackgroundContext()
context.automaticallyMergesChangesFromParent = true
return context
}
newBackgroundContext
メソッドはNSPersistentContainer
の持つメソッドでバックグラウンドスレッド(プライベートキュー)用のContext(NSManagedObjectContext)を作成する役割を持っています。ここで生成されるContext
はメインで使用しているContext
とは別物なので、データを変更しても即座にメインのContext
にマージされないため、適切なタイミングで明示的に変更を保存する必要があります。
またnewBackgroundContext
で生成したContext
からデータ取得や追加を行う際はperform
またはperformAndWait
ブロックの中で操作する必要があるようです。
performメソッド
公式リファレンス:NSManagedObjectContext.performメソッド
func perform(_ block: @escaping () -> Void)
perform
はNSManagedObjectContext
が持つメソッドで指定されたコンテキストでブロック内の処理を実行する役割を持っています。非同期で処理を行い、指定されたコンテキストのスレッドで実行されます。
public func newEntity<T: NSManagedObject>(context: NSManagedObjectContext, completion: @escaping (T) -> Void) {
context.perform {
let entity = NSEntityDescription.entity(forEntityName: String(describing: T.self), in: context)!
let newObject = T(entity: entity, insertInto: context)
completion(newObject)
}
}
performAndWaitメソッド
公式リファレンス:NSManagedObjectContext.performAndWaitメソッド
performAndWait
メソッドはperform
と同じ役割を持っていますが、異なるのは同期的に実行される部分です。そのためブロック内の処理が完了するまで、現在のスレッドがブロックされるのでメインスレッドでの使用には注意が必要です。ただ利点として先ほど完了ハンドラーで受け取っていた結果を返り値として受け取ることができるようになります。
public func newEntity<T: NSManagedObject>(context: NSManagedObjectContext) -> T {
var result: T!
context.performAndWait {
let entity = NSEntityDescription.entity(forEntityName: String(describing: T.self), in: context)!
result = T(entity: entity, insertInto: context)
}
return result
}
ご覧いただきありがとうございました。