Ja, dies ist das erwartete Verhalten. Eine Lösung besteht darin, Ihre Anforderungen in eine benutzerdefinierte, asynchrone NSOperation
Unterklasse zu packen und dann mithilfe maxConcurrentOperationCount
der Operationswarteschlange die Anzahl der gleichzeitigen Anforderungen anstelle der zu steuernHTTPMaximumConnectionsPerHost
Parameters .
Das ursprüngliche AFNetworking hat die Anforderungen in Betrieb genommen, was dies trivial machte. Die NSURLSession
Implementierung von AFNetworking hat dies jedoch nie getan, ebenso wenig wie Alamofire.
Sie können das einfach Request
in eine NSOperation
Unterklasse einschließen. Zum Beispiel:
class NetworkOperation: AsynchronousOperation {
private let urlString: String
private var networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)?
weak var request: Alamofire.Request?
init(urlString: String, networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)? = nil) {
self.urlString = urlString
self.networkOperationCompletionHandler = networkOperationCompletionHandler
super.init()
}
override func main() {
request = Alamofire.request(urlString, method: .get, parameters: ["foo" : "bar"])
.responseJSON { response in
self.networkOperationCompletionHandler?(response.result.value, response.result.error)
self.networkOperationCompletionHandler = nil
self.completeOperation()
}
}
override func cancel() {
request?.cancel()
super.cancel()
}
}
Wenn ich dann meine 50 Anfragen initiieren möchte, würde ich so etwas tun:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2
for i in 0 ..< 50 {
let operation = NetworkOperation(urlString: "http://example.com/request.php?value=\(i)") { responseObject, error in
guard let responseObject = responseObject else {
print("failed: \(error?.localizedDescription ?? "Unknown error")")
return
}
print("responseObject=\(responseObject)")
}
queue.addOperation(operation)
}
Auf diese Weise werden diese Anforderungen durch die eingeschränkt maxConcurrentOperationCount
, und wir müssen uns keine Sorgen machen, dass die Anforderungen abgelaufen sind.
Dies ist eine beispielhafte AsynchronousOperation
Basisklasse, die sich um die KVN kümmert, die der asynchronen / gleichzeitigen NSOperation
Unterklasse zugeordnet ist:
import Foundation
public class AsynchronousOperation : Operation {
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")
}
}
public func completeOperation() {
if isExecuting {
isExecuting = false
}
if !isFinished {
isFinished = true
}
}
override public func start() {
if isCancelled {
isFinished = true
return
}
isExecuting = true
main()
}
override public func main() {
fatalError("subclasses must override `main`")
}
}
import Foundation
extension NSLocking {
func withCriticalScope<T>(block: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try block()
}
}
Es gibt andere mögliche Variationen dieses Musters, aber stellen Sie einfach sicher, dass Sie (a) zurückkehren true
für asynchronous
; und (b) Sie veröffentlichen die erforderliche isFinished
und die isExecuting
KVN wie im Abschnitt Konfigurieren von Vorgängen für die gleichzeitige Ausführung des Concurrency Programming Guide: Operation Queues beschrieben .
, not
timeoutIntervalForRequest " festlegen?