【问题标题】:When to use do-catch block using Swift何时使用 Swift 使用 do-catch 块
【发布时间】:2017-12-02 03:44:59
【问题描述】:

在从文件中读取 JSON 数据的以下场景中,我有以下代码块:

// Fetch URL
let url = Bundle.main.url(forResource: "sampleJSON", withExtension: "json")!

// Load Data
let data = try! Data(contentsOf: url)

// Deserialize JSON
let json = try! JSONSerialization.jsonObject(with: data, options: [])

这段代码本身是否正确,或者将其包含在 do-catch 块中是否会更好?我之所以问,是因为我在使用 URLSession 从 Web 中提取数据时看到了一些场景,其中开发人员在 do-catch 块内执行 JSONSerialization。是否有理由在使用 URLSession 时这样做,而不是在简单地从文件中提取 JSON 数据时这样做?这种情况的最佳做法是什么?

【问题讨论】:

  • 如果您的方法抛出或使用try,您应该始终捕获错误?并返回一个可选的,或者如果您不希望可选作为结果,请使用 nil 合并运算符并提供默认值
  • 如果可用,最好允许 do/catch。您永远不会知道服务器的响应是否更改、损坏等。此外,如果出现错误,调试起来更容易。

标签: ios json swift do-catch


【解决方案1】:

1 - 此代码块本身是否正确,或者将其包含在 do-catch 块中是否更好?

A:这个代码是正确的。如果您的 sampleJSON.json 文件在您的包中并且 JSON 文件中的数据格式正确并且 JSONSerialization 成功解析提供的数据,它将起作用。 p>

2 - 我问这个问题是因为我已经看到使用 URLSession 从 Web 提取数据的场景,开发人员在 do-catch 块内执行 JSONSerialization。是否有理由在使用 URLSession 时这样做,而不是在简单地从文件中提取 JSON 数据时这样做?这种情况的最佳做法是什么?

A:do-catch 语句在使用来自 Web 的数据(在这种情况下为 JSON)时更常见,因为 API 可能因任何原因而中断(必须显示的数据的错误规范,Web 应用程序中的错误本身等),如果发生这种情况,我们不希望我们的应用程序崩溃。

我说 CRASH 是因为您使用了 !,它不会将错误传播到应用程序的上层,它会尝试强制操作,如果失败会使应用程序崩溃。

此时您可能已经意识到,在使用捆绑包中的数据时看不到 do-catch 语句的原因是应用程序开发人员自己提供了 JSON,所以我假设您确定文件的内容,但我仍然会使用 do-catch 语句,因为可能会出现问题并且不希望我的应用程序因为这样的愚蠢事情而崩溃。

TL;DR

我建议始终使用带有 throws/rethrows 甚至 ? 的错误传播,这样您就可以测试 nil 结果。

我写了一个small article here ,其中包含一些提示以及它在 Swift 2.1 中的工作原理,在 Swift 3.1 中没有太大变化,因此您可以用来研究 do-catch 语句。

我会像这样重写你提供的代码:

警告:未经测试的代码

enum JSONFromFileError: Error {
    case fileNotInBundle(String)
    case deserializationError
    case getDataError(URL)
}

func json(from file: String) throws -> Any {
    // Fetch URL in Bundle
    guard let url = Bundle.main.url(forResource: file, withExtension: "json") else {
        throw JSONFromFileError.fileNotInBundle(file)
    }

    // Load Data from url
    guard let data = try? Data(contentsOf: url) else { 
        throw JSONFromFileError.getDataError(url)
    }

    // Deserialize JSON
    guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
        throw JSONFromFileError.deserializationError
    }

    return json
}

do {
    let myJsonObject = try json(from: "sampleJSON")
    print(myJsonObject)
} catch let error {
    print(error)
}

【讨论】:

    【解决方案2】:

    您通常应该为throwable 的每个函数使用一个try-catch 块。使用您当前的代码,如果您的任何一个 try 块失败(无法从 url 下载数据或它不是有效的 json),强制尝试将失败并导致运行时异常。

    let url = Bundle.main.url(forResource: "sampleJSON", withExtension: "json")!
    do {
        let data = try Data(contentsOf: url)
        let json = try JSONSerialization.jsonObject(with: data, options: [])
    } catch {
        //handle error
    }
    

    如果你不关心函数会抛出的错误,你可以使用try?,它在函数抛出错误时返回nil。这样你的代码就不会崩溃了。

    guard let data = try? Data(contentsOf: url) else {return}
    guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else {return}
    

    【讨论】:

    • 强制解包 URL 不是一个好主意。再说一次,在这种情况下,如果在应用程序包中找不到该文件,您可能希望应用程序崩溃,因为这是一个开发错误。
    • 我什至没有意识到那一行的强制展开,我只专注于强制尝试。一般来说,我也不喜欢在我的代码中包含任何强制解包,但在这种情况下你是对的,URL 只能在开发错误的情况下无效,因此最好将其保留为强制解包.
    【解决方案3】:

    Firstly, 建议我们将任何抛出(使用 try)的函数包装到一个 do-catch 块中。在我自己看来,我还会执行一个 do-catch 块,以防我在我不知情的情况下修改了 sampleJSON 文件,这会导致 JSON 的格式受到干扰。

    Secondly,在代码中never use force unwrap at all绝对是个好习惯。

    Lastly,我们应该始终捕获序列化异常并抛出给调用者,最终会通知视图抛出错误对话框。

    【讨论】:

      猜你喜欢
      • 2010-12-15
      • 2011-07-09
      • 1970-01-01
      • 2015-08-23
      • 2018-01-22
      • 2016-05-27
      • 2019-09-26
      • 2018-10-10
      • 2017-11-28
      相关资源
      最近更新 更多