首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >任务取消的WKURLSchemeHandler NSInternalInconsistencyException

任务取消的WKURLSchemeHandler NSInternalInconsistencyException
EN

Stack Overflow用户
提问于 2022-08-16 11:34:54
回答 1查看 240关注 0票数 0

我在WKURLSchemeHandler和任务取消方面有问题,并在下面提供了一个示例实现。

问题是,有时在调用webView(_:stop:)之后(以及“停止任务.”)) try Task.checkCancellation()没有抛出,或者已经被调用了(我不确定),所以urlSchemeTask.didReceivedidFinish中的一个可以像这样让应用程序崩溃:

代码语言:javascript
复制
Stopping task <WKURLSchemeTaskImpl: 0x7fd445c209c0>
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This task has already been stopped'

有注释的示例实现:

代码语言:javascript
复制
import WebKit

class AsyncURLSchemeHandler: NSObject, WKURLSchemeHandler {
    private var pendingTasks = [ObjectIdentifier: TaskItem]()
    
    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        guard let task = pendingTasks.removeValue(forKey: urlSchemeTask.id) else { return }
        print("Stopping task \(urlSchemeTask)")
        task.stop()
    }

    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        let task = Task { [weak self] in
            var request = urlSchemeTask.request
        
            // Do some mutation on the request
            
            do {
                try Task.checkCancellation()
                
                 // Conditionally get a URLSession 
                let session: URLSession 
                
                // Fire off the request
                let (data, response) = try await session.data(for: request)
                
                await Task.yield()
                try Task.checkCancellation()

                // Report back to the scheme task
                // Either of these !! may crash in this implementation
                urlSchemeTask.didReceive(response) // !!
                urlSchemeTask.didReceive(data) // !!
                urlSchemeTask.didFinish() // !!
                
            } catch is CancellationError {
                // Do not call didFailWithError, didFinish, or didReceive in this case
                print("Task for WKURLSchemeTask \(urlSchemeTask) has been cancelled")
            } catch {
                if !Task.isCancelled {
                    // !! This can crash, too
                    urlSchemeTask.didFailWithError(error)
                }
            }
            
            self?.pendingTasks.removeValue(forKey: urlSchemeTask.id)
        }
        
        pendingTasks[urlSchemeTask.id] = .init(urlSchemeTask: urlSchemeTask, task: task)
    }
}

private extension WKURLSchemeTask {
    var id: ObjectIdentifier {
        ObjectIdentifier(self)
    }
}

private struct TaskItem {
    enum Error: Swift.Error {
        case manualCancellation
    }
    
    let urlSchemeTask: WKURLSchemeTask
    let task: Task<Void, Never>
    
    /// Should be called when urlSchemeTask has been stopped by the system
    /// Calling anything on the urlSchemeTask afterwards would result in an exception
    func stop() {
        task.cancel()
    }
    
    /// Should be called when the urlSchemeTask should be stopped manually
    func cancel() {
        task.cancel()
        urlSchemeTask.didFailWithError(Error.manualCancellation)
    }
}

有人能帮我避免这些车祸吗?

这是一个交叉的:https://developer.apple.com/forums/thread/712430

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-02 10:25:00

要解决这个问题,我执行以下操作:

我将挂起的任务状态封装在一个参与者cancellation.

  • I中,我在该参与者中使用了一个Set<ObjectIdentifier>,并检查了contains,而不是使用任务

最重要的是:

  • 主要问题超出了这个问题的范围,我重构了围绕WebView生命周期的东西.
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73373342

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档