评估错误是否为 NSError 总是成功,而将错误转换为 NSError 会给出编译器错误

问题描述

我们遇到了错误对象的问题,该对象在 Crashlytics 的 Objective-c 代码中崩溃,因为它不响应 userInfoNSError 的成员),在调查时我偶然发现了这个奇怪的问题Swift 中的行为。

在操场上,我尝试创建一个实现 SwiftError 协议的类 Error

class SwiftError: Error {
}

let sError = SwiftError()

if sError is NSError { // Generates a warning: 'is' test is always true
    print("Success")
} else {
    print("Fail")
}
// Prints Fail

let nsError = sError as NSError
//Compiler Error: 'SwiftError' is not convertible to 'NSError'; did you mean to use 'as!' to force downcast?
  • 检查 SwiftError 是否为 NSError 会给出警告,表明它总是成功但在运行时失败。
  • 将 SwiftError 转换为 NSError 会导致编译器错误

有人可以帮我解释为什么会发生这种情况,我怎么知道实现 Error 协议的类实际上是否是 NSError

谢谢!

解决方法

NSError 和 Error 之间桥接的性质有些奇怪。它们被桥接用于通信目的——也就是说,它们可以在 Swift 和 Cocoa 之间来回移动;来自 Cocoa 的错误 是一个 NSError(并且 NSError 在 Swift 中采用了 Error 协议,以允许这一点);但是您声明为符合 Error 的 class 本身不是一个 NSError。

因为它不响应 userInfo

如果为了 Cocoa 的利益,您需要 Swift Error 类型携带 userInfo 信息,那么您正在寻找 CustomNSError 协议。

https://developer.apple.com/documentation/foundation/customnserror

,

这在我看来像是一个错误,我已将其归档为 SR-14322

根据SE-0112 Improved NSError Bridging

每个符合 Error 协议的类型都隐式地桥接到 NSError。

这适用于结构和枚举类型,但显然不适用于类类型。

您可以用 class SwiftError: Error 替换您的 struct SwiftError: Error 来解决问题。如果由于某种原因这是不可能的,那么以下“技巧”在我的测试中起作用:

let nsError = sError as Error as NSError

编译并且给出了预期的结果:

class SwiftError: Error {}

extension SwiftError: CustomNSError {
    public static var errorDomain: String { "MyDomain" }
    public var errorCode: Int { 13 }
    public var errorUserInfo: [String : Any] { ["Foo" : "Bar" ] }
}

let sError = SwiftError()

let nsError = sError as Error as NSError
print(nsError.userInfo)
// Output: ["Foo": "Bar"]

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...