对于Mac OS 10.11及更高版本,我拥有大量的Swift 3代码(使用Xcode 8.2.1)。有许多过程,其中包括GUI应用程序和后台服务。两者都使用自定义的URLProtocol
,该代码在框架中实现(由应用程序和服务导入)。该协议(protocol)有时可能会生成符合enum
的Error
实例,并会对其进行适当的捕获和处理(通常通过使用URLProtocolClient将其扔到尝试提出请求的URLSession上)。
在调试器中徘徊显示,当运行时
Error
自动转换为NSError
时,将发生此崩溃。我在代码中明确地添加了这个强制类型转换,并且肯定的是,现在我在同一行上遇到了同样的崩溃。我看到了Swift 3.1: Crash when custom error is converted to NSError to access its domain property,但不适用于这里:
Error
扩展为CustomNSError
(以及LocalizedError
作为很好的措施)并实现相关属性-没有帮助。 domain
后发生崩溃。据我所知,这对我来说不是问题。 已经尝试过列出该问题的唯一解决方案后,我有些茫然。我已经调试了好几个小时,却无所事事,只是它似乎发生在
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
,那么导致崩溃的任何原因都不会发生。不知道它是否可以工作,但似乎值得一试... 最佳答案
好的,据我所知这只是Swift运行时的一个错误,但我找到了一种解决方法:对涉及Obj-C代码的所有内容都使用NSError
而不是Swift Error
(以避免隐式强制转换)。因为我已经实现了CustomNSError
,所以很容易在我的toNSError()
枚举上创建一个Error
函数,并将其用于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);
}
}
...
}
关于swift - 将Error转换为NSError时,Swift 3自定义URLProtocol崩溃,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43859552/