是的,这是预期的行为。一种解决方法是将您的请求封装在自定义的异步NSOperation
子类中,然后使用操作队列的maxConcurrentOperationCount
来控制并发请求的数量,而不是使用HTTPMaximumConnectionsPerHost
参数。
原始的AFNetworking很好地将请求封装在操作中,这使得这个问题变得微不足道。但AFNetworking的NSURLSession
实现从未这样做过,Alamofire也是如此。
您可以轻松地将Request
封装在NSOperation
子类中。例如:
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()
}
}
然后,当我想发起50个请求时,我会这样做:
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)
}
那样一来,这些请求将受到“maxConcurrentOperationCount”的限制,我们就不必担心任何请求超时的问题了。
这是一个示例的异步操作基类,可以处理与异步/并发NSOperation子类相关的KVN。
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()
}
}
这种模式还有其他可能的变化,但请确保你 (a) 返回 true
以表示异步执行;(b) 根据《并发编程指南:操作队列》中“配置操作以进行并发执行”部分的要求发布必要的isFinished
和isExecuting
KVN。
timeoutIntervalForResource
而不是timeoutIntervalForRequest
? - mattt