ここおよびその他の SO の質問への回答に記載されているようbeginBackgroundTask
に、アプリがバックグラウンドに移行する場合にのみ使用する必要はありません。逆に、アプリがバックグラウンドに移行したとしても、確実に完了させたい時間のかかる操作には、バックグラウンド タスクを使用する必要があります。
beginBackgroundTask
したがって、あなたのコードは、呼び出しとendBackgroundTask
一貫性のために同じ定型コードの繰り返しで終わる可能性があります。この繰り返しを防ぐために、ボイラープレートを単一のカプセル化されたエンティティにパッケージ化することは確かに合理的です。
それを行うための既存の回答のいくつかが気に入っていますが、最良の方法は Operation サブクラスを使用することだと思います:
Operation を任意の OperationQueue にエンキューし、必要に応じてそのキューを操作できます。たとえば、キューにある既存の操作を途中で自由にキャンセルできます。
やるべきことが複数ある場合は、複数のバックグラウンド タスク操作を連鎖させることができます。操作は依存関係をサポートします。
オペレーション キューは、バックグラウンド キューにすることができます (またそうする必要があります)。したがって、Operationは非同期コードであるため、タスク内で非同期コードを実行することについて心配する必要はありません。(実際、オペレーション内で別のレベルの非同期コードを実行しても意味がありません。そのコードが開始される前にオペレーションが終了するためです。それを行う必要がある場合は、別のオペレーションを使用します。)
考えられる Operation サブクラスは次のとおりです。
class BackgroundTaskOperation: Operation {
var whatToDo : (() -> ())?
var cleanup : (() -> ())?
override func main() {
guard !self.isCancelled else { return }
guard let whatToDo = self.whatToDo else { return }
var bti : UIBackgroundTaskIdentifier = .invalid
bti = UIApplication.shared.beginBackgroundTask {
self.cleanup?()
self.cancel()
UIApplication.shared.endBackgroundTask(bti) // cancellation
}
guard bti != .invalid else { return }
whatToDo()
guard !self.isCancelled else { return }
UIApplication.shared.endBackgroundTask(bti) // completion
}
}
これを使用する方法は明らかですが、そうでない場合は、グローバルな OperationQueue があると想像してください。
let backgroundTaskQueue : OperationQueue = {
let q = OperationQueue()
q.maxConcurrentOperationCount = 1
return q
}()
したがって、典型的な時間のかかるコードのバッチについては、次のようになります。
let task = BackgroundTaskOperation()
task.whatToDo = {
// do something here
}
backgroundTaskQueue.addOperation(task)
時間のかかるコードのバッチを段階に分割できる場合は、タスクがキャンセルされた場合に早期に辞退することをお勧めします。その場合は、閉鎖から時期尚早に戻ってください。クロージャー内からのタスクへの参照は弱い必要があることに注意してください。そうしないと、保持サイクルが発生します。これは人工的な図です。
let task = BackgroundTaskOperation()
task.whatToDo = { [weak task] in
guard let task = task else {return}
for i in 1...10000 {
guard !task.isCancelled else {return}
for j in 1...150000 {
let k = i*j
}
}
}
backgroundTaskQueue.addOperation(task)
バックグラウンド タスク自体が途中でキャンセルされた場合にクリーンアップを行う必要がある場合に備えて、オプションのcleanup
ハンドラー プロパティを用意しました (前の例では使用されていません)。他のいくつかの回答は、それを含めなかったことで批判されました。