一つの問題は、あなたの操作は、単に要求を開始しているが、要求は非同期に実行されているため、操作はすぐに実際に終了する要求を待っていない、完成されていることです。非同期要求が完了するまで操作を完了したくない場合。
オペレーションキューでこれを行う場合は、NSOperation
をサブクラス化し、true
をisAsynchronous
に戻す必要があります。そして、要求を開始するときにisExecuting
を変更し、要求を完了したときにisFinished
を変更し、両方の要求に必要なKVOを行います。これはすべてConcurrency Programming Guide: Defining a Custom Operation Object、特にConfiguring Operations for Concurrent Executionセクションに記載されています。 (このガイドはもう古くなっています(のプロパティは、isAsynchronous
であり、Objective-Cなどに焦点を当てています)。
とにかく、これは私は、この非同期操作の愚かさのすべてをカプセル化するために使用する抽象クラスです:
// AsynchronousOperation.swift
import Foundation
/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
/// necessary and then ensuring that `completeOperation()` is called; or
/// override `cancel` method, calling `super.cancel()` and then cleaning-up
/// and ensuring `completeOperation()` is called.
public class AsynchronousOperation : Operation {
override public var isAsynchronous: Bool { return true }
private let stateLock = NSLock()
private var _executing: Bool = false
override private(set) public var isExecuting: Bool {
get {
return stateLock.withCriticalScope { _executing }
}
set {
willChangeValue(forKey: "isExecuting")
stateLock.withCriticalScope { _executing = newValue }
didChangeValue(forKey: "isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var isFinished: Bool {
get {
return stateLock.withCriticalScope { _finished }
}
set {
willChangeValue(forKey: "isFinished")
stateLock.withCriticalScope { _finished = newValue }
didChangeValue(forKey: "isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func completeOperation() {
if isExecuting {
isExecuting = false
}
if !isFinished {
isFinished = true
}
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
}
そして私は、私は上記の状態変化を同期させることを確認するNSLock
に、このアップルの拡張機能を使用する:
extension NSLock {
/// Perform closure within lock.
///
/// An extension to `NSLock` to simplify executing critical code.
///
/// - parameter block: The closure to be performed.
func withCriticalScope<T>(block:() -> T) -> T {
lock()
let value = block()
unlock()
return value
}
}
次に、例えば、私は今、ネットワーク要求のための操作を作成することができることを行ってた、とにかく
class NetworkOperation: AsynchronousOperation {
let url: URL
let session: URLSession
let requestCompletionHandler: (Data?, URLResponse?, Error?) ->()
init(session: URLSession, url: URL, requestCompletionHandler: @escaping (Data?, URLResponse?, Error?) ->()) {
self.session = session
self.url = url
self.requestCompletionHandler = requestCompletionHandler
super.init()
}
private weak var task: URLSessionTask?
override func main() {
let task = session.dataTask(with: url) { data, response, error in
self.requestCompletionHandler(data, response, error)
self.completeOperation()
}
task.resume()
self.task = task
}
override func cancel() {
task?.cancel()
super.cancel()
}
}
:私はそれを使用していますNetworkOperation
を作成することができ
let queue = OperationQueue()
queue.name = "com.domain.app.network"
let url = URL(string: "http://...")!
let operation = NetworkOperation(session: URLSession.shared, url: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
// do something with `data` here
}
let operation2 = BlockOperation {
print("done")
}
operation2.addDependency(operation)
queue.addOperations([operation, operation2], waitUntilFinished: false) // if you're using command line app, you'd might use `true` for `waitUntilFinished`, but with standard Cocoa apps, you generally would not
注意を、上記の例では、私が追加しました最初の操作がネットワーク要求が完了するまで完了していないことを示すために、何かを印刷した第2の操作を第1の操作に依存させる。
私の例では、元の例のwaitUntilAllOperationsAreFinished
とwaitUntilFinished
のオプションをaddOperations
と使用しないことは明らかです。しかし、これらの要求が完了するまで終了したくないコマンドラインアプリを扱っているので、このパターンは受け入れられます。 (私は、フリーホイール使用で驚いている将来の読者のためにこれを言及していますが、これは一般的にお勧めできません)。
https://github.com/ankitthakur/SwiftNetworkをご覧ください。それはswift3で同時にHTTPリクエストをサポートしています。 –