1

在从文件中读取 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 中提取数据时看到了一些场景,开发人员在其中执行 JSONSerialization 在 do-catch 块内。是否有理由在使用 URLSession 时这样做,而不是在简单地从文件中提取 JSON 数据时这样做?这样的事情的最佳做法是什么?

4

3 回答 3

4

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

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

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

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

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

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

TL;博士

我建议始终将错误传播与 throws/rethrows 一起使用,甚至?可以测试 nil 结果。

我在这里写了一篇小文章,其中包含一些技巧以及它在 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)
}
于 2017-06-28T17:15:11.560 回答
3

通常,您应该为每个函数使用一个 try-catch 块,即throwable. 使用您当前的代码,如果您的任何一个 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?which 返回 a nil。这样你的代码就不会崩溃。

guard let data = try? Data(contentsOf: url) else {return}
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else {return}
于 2017-06-28T16:45:49.937 回答
1

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

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

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

于 2017-06-28T16:47:08.900 回答