2017-05-09 45 views
0

我已经为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,那么无论是造成这次崩溃不会发生。不知道它是否会工作,但它似乎值得一试...

回答

0

好吧,据我可以告诉这只是在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); 
     } 
    } 
... 
} 
相关问题