ここと他のSOの質問への回答で述べたようbeginBackgroundTask
に、アプリがバックグラウンドになるときだけ使用するのは望ましくありません。逆に、あなたがのためのバックグラウンドタスクを使用する必要があります任意のその完成アプリがあっても確実にしたい時間のかかる操作んバックグラウンドに入ります。
したがって、あなたのコードは、呼び出し元に同じ定型コードの繰り返しがちりばめ終わる可能性があるbeginBackgroundTask
とendBackgroundTask
コヒーレント。この繰り返しを防ぐために、ボイラープレートをいくつかの単一のカプセル化されたエンティティーにパッケージ化することは確かに合理的です。
私はそれを行うための既存の回答のいくつかが好きですが、最善の方法はOperationサブクラスを使用することだと思います:
オペレーションを任意のOperationQueueにエンキューし、必要に応じてそのキューを操作できます。たとえば、キュー上の既存の操作を途中でキャンセルすることができます。
やることが複数ある場合は、複数のバックグラウンドタスクの操作を連鎖させることができます。操作は依存関係をサポートします。
操作キューはバックグラウンドキューにすることができます(そうする必要があります)。したがって、操作は非同期コードであるため、タスク内で非同期コードを実行することを心配する必要はありません。(実際、オペレーション内で別のレベルの非同期コードを実行しても、コードが開始する前にオペレーションが完了するため、意味がありません。必要に応じて、別のオペレーションを使用します。)
可能な操作サブクラスは次のとおりです。
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
ハンドラープロパティを提供しました(前の例では使用されていません)。他のいくつかの回答はそれを含まないと非難されました。