【问题标题】:Why can any Error be unconditionally converted to NSError?为什么任何 Error 都可以无条件转换为 NSError?
【发布时间】:2019-01-07 05:33:54
【问题描述】:

很多时候,我会从框架收到一个 Swift Error 对象,它实际上是一个 NSError

为了访问其信息(例如code),我需要将其转换为NSError

(error as NSError).code == ....

为什么这只是一个无条件的as?如果我设计自己的符合Error 的错误类,它不一定是NSError,那么这怎么可能是执行此转换的正确方法呢?

类型系统中是否存在某种特殊情况?这是一个表现得像一个upcast的downcast。

【问题讨论】:

    标签: swift nserror


    【解决方案1】:

    我相信Error 可转换为NSError 的能力是硬编码到编译器中的,而实际的桥接是在 Swift 运行时中实现的。

    runtime/ErrorObject.mm,我发现了这条评论:

    // This implements the object representation of the standard Error
    // type, which represents recoverable errors in the language. This
    // implementation is designed to interoperate efficiently with Cocoa libraries
    // by:
    // - ...
    // - allowing a native Swift error to lazily "become" an NSError when
    //   passed into Cocoa, allowing for cheap Swift to Cocoa interop
    

    还有this function:

    /// Take an Error box and turn it into a valid NSError instance.
    id
    swift::_swift_stdlib_bridgeErrorToNSError(SwiftError *errorObject) {
        ...
    
      // Otherwise, calculate the domain, code, and user info, and
      // initialize the NSError.
      auto value = SwiftError::getIndirectValue(&errorObject);
      auto type = errorObject->getType();
      auto witness = errorObject->getErrorConformance();
    
      NSString *domain = getErrorDomainNSString(value, type, witness);
      NSInteger code = getErrorCode(value, type, witness);
      NSDictionary *userInfo = getErrorUserInfoNSDictionary(value, type, witness);
    
      ...
    }
    

    ErrorHandling.rst document 谈到了基本原理:

    应该可以将符合Error 的任意Swift 枚举转换为NSError,方法是使用限定类型名称作为域键、枚举器作为错误代码,并将有效负载转换为用户数据。

    (文档的某些部分可能已过时。)

    并且this is(我认为)类型检查器中至少有一部分是Error 可转换为NSError 的信息被编码(可能还有更多):

      // Check whether the type is an existential that contains
      // Error. If so, it's bridged to NSError.
      if (type->isExistentialWithError()) {
        if (auto nsErrorDecl = getNSErrorDecl()) {
          // The corresponding value type is Error.
          if (bridgedValueType)
            *bridgedValueType = getErrorDecl()->getDeclaredInterfaceType();
    
          return nsErrorDecl->getDeclaredInterfaceType();
        }
      }
    

    【讨论】:

    • > 枚举数作为错误代码 一个无法确认为 Int 等本机类型的枚举,如何确定枚举数?是否保证按顺序列出案例?
    【解决方案2】:

    这是一个很好的问题。

    我以为我在某处看到“错误类型可以桥接到 NSError”,但那肯定是 Xcode 或在线教程。

    幸运的是,我从swift/NSError.swift 找到了这个。

    // NSError and CFError conform to the standard Error protocol. Compiler
    // magic allows this to be done as a "toll-free" conversion when an NSError
    // or CFError is used as an Error existential.
    extension NSError : Error {
      @nonobjc
      public var _domain: String { return domain }
    
      @nonobjc
      public var _code: Int { return code }
    
      @nonobjc
      public var _userInfo: AnyObject? { return userInfo as NSDictionary }
    
      /// The "embedded" NSError is itself.
      @nonobjc
      public func _getEmbeddedNSError() -> AnyObject? {
        return self
      }
    }
    
    extension CFError : Error {
      public var _domain: String {
        return CFErrorGetDomain(self) as String
      }
    
      public var _code: Int {
        return CFErrorGetCode(self)
      }
    
      public var _userInfo: AnyObject? {
        return CFErrorCopyUserInfo(self) as AnyObject
      }
    
      /// The "embedded" NSError is itself.
      public func _getEmbeddedNSError() -> AnyObject? {
        return self
      }
    }
    

    【讨论】:

    • 感谢您找到这个!它对我来说仍然不是很清楚。它表明NSErrorCFError 都实现了Error,但它仍然没有显示我如何可以采用任意Error 并将100% 安全的转换为NSError - 这就像一个沮丧表现得像一个向上的人。我猜评论的“编译器魔法”部分是解决方案所在。
    • 是的。还有其他相关部分,所以希望接近答案:/
    • 另一个让我困惑的部分是我可以编写一个符合Error 协议的自定义类,我什至不需要在我的类中提供localizedDescription 属性。然而该类编译了,我可以神奇地(使用as NSError)将我的自定义类转换为NSErrorError 好像真的是经过特殊处理的协议。
    【解决方案3】:
    switch error {
    case _ where (error as NSError).domain == self.domain:
       print("error from particular domain")
    default:
       print("default error")
    }
    

    【讨论】:

      猜你喜欢
      • 2017-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多