【问题标题】:Swift 3 custom URLProtocol crashes when converting Error to NSError将 Error 转换为 NSError 时,Swift 3 自定义 URLProtocol 崩溃
【发布时间】:2017-05-09 01:22:39
【问题描述】:

我有大量适用于 Mac OS 10.11 及更高版本的 Swift 3 代码(使用 Xcode 8.2.1)。有许多进程,其中有一个 GUI 应用程序和一个后台服务。这两个都使用自定义的URLProtocol,它在框架中实现(由应用程序和服务导入)。该协议有时可能会生成符合Errorenum 实例,它会适当地捕获和处理这些实例(通常通过使用URLProtocolClient 将它们扔到试图发出请求的URLSession)。

  • 当没有错误时,应用和服务都可以正常工作。
  • 当出现错误时,应用可以正常工作(嗯,正如预期的那样)。
  • 出现错误时,服务会崩溃

通过调试器发现当Error 被运行时自动转换为NSError 时,就会发生这种崩溃。我在我的代码中明确添加了这个演员,果然,我得到了同样的崩溃,现在在那一行。

我看到了Swift 3.1: Crash when custom error is converted to NSError to access its domain property,但这里不适用:

  • 那里的解决方案 - 将 Error 扩展到 CustomNSError(以及 LocalizedError 以进行良好的衡量)并实现相关属性 - 没有帮助。
  • 在获得domain 后发生崩溃;据我所知,这对我来说不是问题。
  • 可能相关:那是在 iOS 上,而不是在 Mac 上。

已经尝试了该问题的唯一列出的解决方案,我有点不知所措。我已经调试了好几个小时了,但它似乎发生在 dyld 的深处。

代码:

enum CrowbarProtocolError: Error {
    case FailedToCreateSocket;
    case PathTooLong;
    case NonSocketFile;
    case SocketNotFound;
...
    case UnrecognizedError(errno: Int32);
}
extension CrowbarProtocolError: LocalizedError {
    public var errorDescription: String? {
        return "Some localized description"
    }
}
extension CrowbarProtocolError: CustomNSError {
    public static var errorDomain: String {
        return "Some Domain Name"
    }
    public var errorCode: Int {
        return 204 //Should be your custom error code.
    }
    public var errorUserInfo: [String: Any] {
        return ["WTF": "Swift?"];
    }
}
...
open class CrowbarUrlProtocol: URLProtocol  {
...
    override open func startLoading() {
        do {
            let sockHandle = try CrowbarUrlProtocol.openSocket();
            let req = try buildRequestData();
            sockHandle.write(req);
            NotificationCenter.default.addObserver(
                self, 
                selector: #selector(self.readCompleted), 
                name: .NSFileHandleReadToEndOfFileCompletion, 
                object: nil);
            sockHandle.readToEndOfFileInBackgroundAndNotify();
        } catch let err {
            Log.warn("CrowbarUrlProtocol request failed with error \(err)");
            // -- In the background service, THIS LINE CRASHES! --
            let err2 = err as NSError;
            Log.warn("As NSError: \(err2)");
            // -- Without the explicit cast, it crashes on this line --
            self.client?.urlProtocol(self, didFailWithError: err);
        }
    }
...
}

我解决这个问题的一个想法就是使用NSErrors 做所有事情(或尽可能多地),因为如果不需要将Error 转换为NSError,那么任何导致这次崩溃的事情都不会发生。不知道它是否会起作用,但似乎值得一试......

【问题讨论】:

    标签: swift macos swift3 crash nserror


    【解决方案1】:

    好的,据我所知,这只是 Swift 运行时中的一个错误,但我找到了一种解决方法:只需将 NSError 用于涉及 Obj-C 代码的所有内容,而不是 Swift Errors(以避免隐式转换)。由于我已经实现了CustomNSError,因此很容易在我的Error 枚举上创建一个toNSError() 函数,并将其用于self.client?.urlProtocol(self, didFailWithError: err) 行。

    enum CrowbarProtocolError: Error, CustomNSError {
        case FailedToCreateSocket;
        case PathTooLong;
    ...
        public func asNSError() -> NSError {
            return NSError(domain: CrowbarProtocolError.errorDomain,
                        code: self.errorCode,
                        userInfo: self.errorUserInfo);
        }
    }
    ...
    open class CrowbarUrlProtocol: URLProtocol  {
    ...
        override open func startLoading() {
            do {
                let sockHandle = try CrowbarUrlProtocol.openSocket();
                let req = try buildRequestData();
                sockHandle.write(req);
                NotificationCenter.default.addObserver(
                    self, 
                    selector: #selector(self.readCompleted), 
                    name: .NSFileHandleReadToEndOfFileCompletion, 
                    object: nil);
                sockHandle.readToEndOfFileInBackgroundAndNotify();
            } catch let err as CrowbarProtocolError {
                Log.warn("CrowbarUrlProtocol request failed with error \(err)");
                self.client?.urlProtocol(self, didFailWithError: err.asNSError());
            }
            catch let err {
                Log.warn("CrowbarUrlProtocol caught non-CrowbarProtocol Error \(err)");
                // This would probably crash the service, but shouldn't ever happen
                self.client?.urlProtocol(self, didFailWithError: err);
            }
        }
    ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-19
      • 2017-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-04
      • 1970-01-01
      • 2016-04-19
      相关资源
      最近更新 更多