Swift 3自定义URLProtocol在将错误转换为NSError时崩溃
我已经为Mac OS 10.11及更高版本(使用Xcode 8.2.1)提供了一个相当庞大的Swift 3代码体。有许多进程,其中包括GUI应用程序和后台服务。这两个都使用自定义URLProtocol
,它在框架中实现(由应用程序和服务导入)。该协议有时可能生成符合Error
的实例,它会捕获并适当地处理它(通常通过使用URLProtocolClient将它们投掷到尝试发出请求的URLSession)。Swift 3自定义URLProtocol在将错误转换为NSError时崩溃
- 当没有错误时,应用程序和服务都可以正常工作。
- 当有是出现错误时,该应用程序正常工作(当然,如预期的那样)。
- 当出现错误时,服务崩溃。
通过调试徘徊已经表明,当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);
}
}
...
}
一个想法我对解决这个只是做一切(或尽可能多地)使用NSError
S,理由是如果有永远不需要的Error
转换为一个NSError
,那么无论是造成这次崩溃不会发生。不知道它是否会工作,但它似乎值得一试...
好吧,据我可以告诉这只是在Swift的运行时错误,但我找到了一个解决方法:只需使用NSError
的一切涉及Obj-C代码而不是Swift Error
s(以避免隐式转换)。由于我已经实施了CustomNSError
,因此只需在我的Error
enum上创建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);
}
}
...
}