하나의 문제는 당신의 작업은 단순히 요청을 시작하지만, 요청이 비동기 적으로 수행되기 때문에, 작업이 즉시 요청이 완료 될 때까지 실제로 대기, 완료된다는 점이다. 비동기 요청이 완료 될 때까지 조작을 완료하지 않으려 고합니다.
작업 대기열에서이 작업을 수행하려는 경우 NSOperation
서브 클래스를 지정해야하고 true
을 isAsynchronous
에서 반환해야한다는 것이 트릭입니다. 그런 다음 요청을 시작하면 isExecuting
을 변경하고 요청을 완료하면 isFinished
을 변경하여 두 요청에 모두 필요한 KVO를 수행합니다. 이것은 모두 Concurrency Programming Guide: Defining a Custom Operation Object, 특히 Configuring Operations for Concurrent Execution 섹션에 설명되어 있습니다. 참고로이 가이드는 다소 구식입니다 (대체 된 isConcurrent
속성은 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
참고, 내가 추가 된 네트워크 요청이 완료 될 때까지 첫 번째 작업이 완료되지 않았 음을 보여주기 위해 첫 번째 작업에 종속적으로 만드는 무언가를 방금 인쇄 한 두 번째 작업.
분명히 원래 예제의 waitUntilAllOperationsAreFinished
이나 waitUntilFinished
옵션을 addOperations
으로 사용하지 않을 것입니다. 그러나 이러한 요청이 완료 될 때까지 종료하고 싶지 않은 명령 행 응용 프로그램을 다루기 때문에이 패턴을 사용할 수 있습니다. (저는 보통 waitUntilFinished
의 무료 휠링 사용에 놀란 미래의 독자들을 위해 이것을 언급합니다.)
https://github.com/ankitthakur/SwiftNetwork에서 볼 수 있습니다. swift3에서 동시 http 요청을 지원합니다. –