尽管“尝试”,为什么 swift 会抛出异常?

Gom*_*rek 2 ios swift

尽管进行了try?施工,我还是在我的应用程序中发现了一些崩溃。记录的 firebase crashlytics

Fatal Exception: NSInvalidArgumentException
Invalid number value (infinite) in JSON write
Run Code Online (Sandbox Code Playgroud)

这是一个测试示例

        let avgSpeed = 0.1 / 0
        print(avgSpeed)
        let data = ["average_speed" : avgSpeed]
        if let body = try? JSONSerialization.data(withJSONObject: [data]) {
            print("success")
        } else {
            print("unable to make body for call")
        }
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

iOS 13,快速 4

Rob*_*ier 5

try?不捕获异常。它捕获抛出的错误。这些在 Swift 中是不同的。异常处于 Objective-C 级别,并且根本无法被 Swift捕获(在大多数情况下,它们也无法在 ObjC 中安全地捕获,但这是一个不同的讨论)。

这种情况下的解决方案是使用 JSONEncoder 而不是 JSONSerialization。JSONEncoder 是一个纯 Swift 系统。JSONSerialization 是从 ObjC 桥接的。

let body = try? JSONEncoder().encode([data])
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅处理错误

Swift 中的错误处理类似于其他语言中的异常处理,使用 try、catch 和 throw 关键字。与许多语言(包括 Objective-C)中的异常处理不同,Swift 中的错误处理不涉及展开调用堆栈,这是一个计算成本很高的过程。因此,throw 语句的性能特征与 return 语句的性能特征相当。

如果您想使用 JSONSerialization,重要的是要认识到以这种方式调用它是一个编程错误。该异常旨在使程序崩溃(即使在 ObjC 中)。编写此代码的正确方法是:

if JSONSerialization.isValidJSONObject([data]), // <=== first, check it is valid
    let body = try? JSONSerialization.data(withJSONObject: [data]) {
    print("success")
} else {
    print("unable to make body for call")
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅文档

如果 obj 不会生成有效的 JSON,则会引发异常。此异常在解析之前抛出,表示编程错误,而不是内部错误。在使用 isValidJSONObject(_:) 调用此方法之前,您应该检查输入是否会生成有效的 JSON。

JSONSerialization 抛出的错误只是表明序列化程序中的内部错误,而不是尝试对无效对象进行编码:

错误

如果发生内部错误,返回时包含一个 NSError 对象,其代码 NSPropertyListWriteInvalidError 描述了问题。