Có, đây là hành vi mong đợi. Một giải pháp là bao bọc các yêu cầu của bạn trong lớp con NSOperation
tùy chỉnh, không đồng bộ và sau đó sử dụng maxConcurrentOperationCount
của hàng đợi hoạt động để kiểm soát số lượng yêu cầu đồng thời thay vì tham số HTTPMaximumConnectionsPerHost
.
AFNetworking ban đầu đã thực hiện một công việc tuyệt vời bao gồm các yêu cầu trong hoạt động, điều này thực hiện tầm thường này. Nhưng việc thực hiện NSURLSession
của AFNetworking không bao giờ làm điều này, cũng không phải Alamofire.
Bạn có thể dễ dàng quấn Request
trong lớp con NSOperation
. Ví dụ:
class NetworkOperation: AsynchronousOperation {
// define properties to hold everything that you'll supply when you instantiate
// this object and will be used when the request finally starts
//
// in this example, I'll keep track of (a) URL; and (b) closure to call when request is done
private let urlString: String
private var networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)?
// we'll also keep track of the resulting request operation in case we need to cancel it later
weak var request: Alamofire.Request?
// define init method that captures all of the properties to be used when issuing the request
init(urlString: String, networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)? = nil) {
self.urlString = urlString
self.networkOperationCompletionHandler = networkOperationCompletionHandler
super.init()
}
// when the operation actually starts, this is the method that will be called
override func main() {
request = Alamofire.request(urlString, method: .get, parameters: ["foo" : "bar"])
.responseJSON { response in
// do whatever you want here; personally, I'll just all the completion handler that was passed to me in `init`
self.networkOperationCompletionHandler?(response.result.value, response.result.error)
self.networkOperationCompletionHandler = nil
// now that I'm done, complete this operation
self.completeOperation()
}
}
// we'll also support canceling the request, in case we need it
override func cancel() {
request?.cancel()
super.cancel()
}
}
Sau đó, khi tôi muốn để bắt đầu 50 yêu cầu của tôi, tôi muốn làm một cái gì đó như thế này:
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 {
// handle error here
print("failed: \(error?.localizedDescription ?? "Unknown error")")
return
}
// update UI to reflect the `responseObject` finished successfully
print("responseObject=\(responseObject)")
}
queue.addOperation(operation)
}
Bằng cách đó, những yêu cầu đó sẽ được hạn chế bởi maxConcurrentOperationCount
, và chúng tôi không cần phải lo lắng về bất kỳ yêu cầu thời gian ra ..
Đây là một lớp cơ sở dụ AsynchronousOperation
, mà sẽ chăm sóc của KVN gắn liền với không đồng bộ/đồng thời NSOperation
lớp con:
//
// AsynchronousOperation.swift
//
// Created by Robert Ryan on 9/20/14.
// Copyright (c) 2014 Robert Ryan. All rights reserved.
//
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()
}
override public func main() {
fatalError("subclasses must override `main`")
}
}
/*
Copyright (C) 2015 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
An extension to `NSLock` to simplify executing critical code.
From Advanced NSOperations sample code in WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/
From https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip
*/
import Foundation
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: (Void) -> T) -> T {
lock()
let value = block()
unlock()
return value
}
}
Có các biến thể khác có thể có của mẫu này, nhưng chỉ đảm bảo rằng bạn (a) trả lại true
cho asynchronous
; và (b) bạn đăng cần thiết isFinished
và isExecuting
KVN như đã nêu Định cấu hình hoạt động để thực hiện đồng thời phần của Concurrency Programming Guide: Operation Queues.
Có lẽ những gì bạn thực sự muốn đặt là ' timeoutIntervalForResource', không phải' timeoutIntervalForRequest'? – mattt
Cảm ơn, nhưng tôi đã thử cả hai và điều tương tự vẫn xảy ra. – Hannes