【问题标题】:Evaluating if Error is NSError always succeeds while casting Error as NSError gives a compiler error评估 Error 是否为 NSError 总是成功,同时将 Error 转换为 NSError 会导致编译器错误
【发布时间】:2021-03-08 17:32:25
【问题描述】:

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

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

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

谢谢!

【问题讨论】:

  • 不要发布屏幕截图发布您的代码
  • 你可以有条件地转换你的错误对象as? NSError
  • 顺便说一句,您为什么需要将Error 转换为NSError?如果您展示您的实际问题,我们可以提供更好的替代方案
  • stackoverflow.com/questions/41189606/… 的可能重复项——我可能不应该回答。
  • 这对我来说似乎是一个错误。仅当sError 是类的实例时,转换sError as NSError 才会失败,而是使用结构或枚举进行编译。即使有一个类,let nsError = sError as Error as NSError 也会编译。

标签: ios swift xcode swift5 nserror


【解决方案1】:

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

因为它没有回复userInfo

如果您需要 Swift 错误类型来携带 userInfo 信息以使 Cocoa 受益,那么您正在寻找 CustomNSError 协议。

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

【讨论】:

  • 有趣的是,如果sErrorstruct SwiftError: Errorenum SwiftError: Error 的一个实例,let nsError = sError as NSError 确实可以编译——它只是无法使用class SwiftError: Error 编译
  • @MartinR 我认为它就像 Hashable:Swift 可以将一致性注入枚举或结构,但不能注入类。事实上,我确实考虑过在我的回答中对此发表评论......
  • 但是let nsError = sError as Error as NSError 确实可以编译和工作。对我来说它看起来像一个错误,但我可能完全错了。于是决定请教专家:forums.swift.org/t/…
【解决方案2】:

这对我来说似乎是一个错误,我已将其归档为 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"]

【讨论】:

    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    相关资源
    最近更新 更多