swift - 观察 URLSession 操作队列计数?

我以这种方式使用 URLSession 设置:

public struct NetworkSession {
    
    public var session: URLSession
    public let sessionOperationQueue = OperationQueue()
    
    public init() {
        
        let sessionConfiguration = URLSessionConfiguration.default
        sessionConfiguration.timeoutIntervalForRequest = 20
        sessionOperationQueue.maxConcurrentOperationCount = OperationQueue.defaultMaxConcurrentOperationCount
        sessionOperationQueue.qualityOfService = .userInitiated
        session = URLSession(configuration: sessionConfiguration, delegate: nil, delegateQueue:sessionOperationQueue)
            
    }

    .....

}

我想观察队列中找到的任务数。

我尝试使用组合:

sessionOperationQueue.publisher(for: \.operationCount).sink { count in
        print("operations count: \(count)")
    }
    .store(in: &subscribers)

但这只会在初始化时打印 0 并且永远不会在请求开始和完成时更新。

如何监控队列中找到的任务数量?

回答1

tl;博士

观察会话队列上的操作计数不会达到你想要的效果。

  • 在传统的 URLSession 代码中,队列用于单个委托方法,而不是包装整个请求-响应。
  • 如果使用新的 async-await 演绎版,则根本不使用操作队列(鉴于先前的观察,这没有实际意义)。

底线,虽然 URLSession 有一种方法可以查询正在进行的待处理请求,但它没有,AFAIK,对此具有可观察的属性(当然,除非您放弃完成处理程序并仅使用委托再现)。因此,如果您想动态跟踪未决请求的计数,只需自己跟踪即可。异步自定义 Operation 子类模式似乎有点矫枉过正(但在 https://stackoverflow.com/a/32322851/1271826Operation 部分中进行了概述)。最简单的方法是通过一种方法简单地路由我的所有网络请求,该方法在请求进入时递增计数器并在完成时递减它。

带有代码示例的长答案

您可以https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift(见下文),但这不会达到您想要的效果。这不是包装整个网络请求和响应的操作,而是针对单个会话委托和完成处理程序回调的单个操作。

例如,考虑:

class ViewController: UIViewController {

    lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 20
        return URLSession(configuration: configuration, delegate: nil, delegateQueue: queue)
    }()

    let queue: OperationQueue = {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        queue.qualityOfService = .userInitiated
        return queue
    }()

    var observer: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        observer = queue.observe(\.operationCount, options: .new) { queue, change in
            print("Observer reported operationCount =", change.newValue!)
        }

        for i in 1000..<1010 {
            let url = URL(string: "https://httpbin.org/get?value=\(i)")!
            session.dataTask(with: url) { data, _, error in
                guard let data = data else {
                    print(error ?? URLError(.badServerResponse))
                    return
                }

                print("Request", i, "returned", data.count, "bytes")
            }.resume()
        }
    }
}

这会产生:

Observer reported operationCount = 1
Observer reported operationCount = 2
Observer reported operationCount = 3
Request 1000 returned 405 bytes
Observer reported operationCount = 4
Observer reported operationCount = 3
Request 1002 returned 405 bytes
Observer reported operationCount = 2
Request 1004 returned 405 bytes
Observer reported operationCount = 1
Request 1001 returned 405 bytes
Observer reported operationCount = 0
Observer reported operationCount = 1
Observer reported operationCount = 2
Request 1006 returned 405 bytes
Observer reported operationCount = 3
Observer reported operationCount = 2
Observer reported operationCount = 3
Request 1005 returned 405 bytes
Observer reported operationCount = 4
Observer reported operationCount = 3
Observer reported operationCount = 4
Request 1003 returned 405 bytes
Observer reported operationCount = 3
Request 1008 returned 405 bytes
Observer reported operationCount = 2
Request 1007 returned 405 bytes
Observer reported operationCount = 1
Request 1009 returned 405 bytes
Observer reported operationCount = 0

请注意,您永远不会看到它承认有十个请求待处理。 operationCount 正在报告委托队列中的内容,这不是您要查找的内容。

顺便说一下,在上面,委托队列是串行的(如 https://developer.apple.com/documentation/foundation/urlsession/1411597-init 中所建议的)。它是一个允许并发网络请求的串行队列这一事实进一步证明,不存在包装整个请求的操作,而是针对单个委托回调的操作。

顺便说一句,如果您使用新的 async-await URLSession 方法,则根本不会使用操作队列。这是有道理的(考虑到它使用的是新的并发系统),但此时文档中没有说明。无论如何,以下内容不会触发任何操作计数更改:

func startRequests() async throws {
    try await withThrowingTaskGroup(of: Void.self) { group in
        for i in 0..<4 {
            let url = URL(string: "https://httpbin.org/get?value=\(i)")!
            group.addTask {
                let (data, _) = try await self.session.data(from: url)
                print("Request", i, "returned", data.count, "bytes")
            }
        }

        try await group.waitForAll()
    }
}

但这没有实际意义,因为 URLSession 操作队列无论如何都无法实现您想要的。

相似文章

最新文章