Ja, dies ist das erwartete Verhalten. Eine Lösung besteht darin, Ihre Anforderungen in eine benutzerdefinierte, asynchrone NSOperationUnterklasse zu packen und dann mithilfe maxConcurrentOperationCountder 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 NSURLSessionImplementierung von AFNetworking hat dies jedoch nie getan, ebenso wenig wie Alamofire.
Sie können das einfach Requestin eine NSOperationUnterklasse 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 AsynchronousOperationBasisklasse, die sich um die KVN kümmert, die der asynchronen / gleichzeitigen NSOperationUnterklasse 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 truefür asynchronous; und (b) Sie veröffentlichen die erforderliche isFinishedund die isExecutingKVN wie im Abschnitt Konfigurieren von Vorgängen für die gleichzeitige Ausführung des Concurrency Programming Guide: Operation Queues beschrieben .
, nottimeoutIntervalForRequest " festlegen?