将Error转换为NSError时,Swift 3自定义URLProtocol崩溃 [英] Swift 3 custom URLProtocol crashes when converting Error to NSError

查看:335
本文介绍了将Error转换为NSError时,Swift 3自定义URLProtocol崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got a rather large body of Swift 3 code for Mac OS 10.11 and up (using Xcode 8.2.1). There are a number of processes, among them a GUI application and a background service. Both of these use a custom URLProtocol, which is implemented in a framework (imported by both application and service). The protocol sometimes may generate instances of an enum that conforms to Error, which it catches and handles appropriately (generally by using the URLProtocolClient to toss them up to the URLSession trying to make the request).

  • 没有错误时,应用程序和服务都可以正常工作.
  • 出现 错误时,该应用程序可以正常运行(按预期方式运行).
  • 出现错误时,服务崩溃.
  • When there's no error, both the app and the service work fine.
  • When there is an error, the app works fine (well, as expected).
  • When there is an error, the service crashes.

在调试器中的游荡表明,当Error由运行时自动转换为NSError时,会发生此崩溃.我在代码中明确地添加了这个强制类型转换,并且肯定的是,我现在在那一行上遇到了同样的崩溃.

Wandering through the debugger has shown that this crash is occurring when the Error is automatically converted into an NSError by the runtime. I added this cast explicitly in my code, and sure enough, I get the same crash, now on that line.

我看到了

I saw Swift 3.1: Crash when custom error is converted to NSError to access its domain property, but it doesn't apply here:

  • 那里的解决方案-将Error扩展到CustomNSError(出于良好的考虑,还扩展了LocalizedError)并实现相关属性-并没有帮助.
  • 在获得domain之后发生崩溃;据我所知,这对我来说不是问题.
  • 可能与之相关:在iOS而非Mac上.
  • The solution there - extending the Error to a CustomNSError (and LocalizedError for good measure) and implementing the relevant properties - didn't help.
  • The crash occurs after the domain has been obtained; as far as I can tell that was not a problem for me.
  • Possibly relevant: that was on iOS, not Mac.

已经尝试过列出该问题的唯一解决方案,但我有些茫然.我已经调试了好几个小时,却一无所获,只是它似乎发生在dyld内胆的某个地方.

Having already tried the only listed solution to that question, I'm at something of a loss. I've been debugging this for hours without getting anywhere except that it seems to happen somewhere deep in the guts of 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);
        }
    }
...
}

我解决这个问题的一个主意是使用NSError做所有(或尽可能多的)操作,理由是如果不需要将Error转换为NSError,导致崩溃的任何原因都不会发生.不知道它是否可以工作,但似乎值得尝试...

One idea I have for solving this is just doing everything (or, as much as possible) using NSErrors, on the grounds that if there's never a need to convert an Error to an NSError, then whatever is causing this crash won't happen. No idea if it'll work but it seems worth a try...

推荐答案

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

OK, as far as I can tell this is just a bug in Swift's runtime, but I found a work-around: just use NSError for everything involving Obj-C code rather than Swift Errors (to avoid the implicit cast). Since I already was implementing CustomNSError, it was easy to just create a toNSError() function on my Error enum, and use that for the self.client?.urlProtocol(self, didFailWithError: err) lines.

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);
        }
    }
...
}

这篇关于将Error转换为NSError时,Swift 3自定义URLProtocol崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆