Xcode报错:Call can throw, but it is not marked with ‘try’ and the error is not handled
Xcode报错:Call can throw, but it is not marked with ‘try’ and the error is not handled

Xcode报错:Call can throw, but it is not marked with ‘try’ and the error is not handled

问题描述

本篇文章主要是讲述如何在实践中学习并使用try关键词。

首先,我在学习JSONEncoder时,想要尝试输出经过JSONEncoder编码后的内容,因此,我写了下面这段输出json的代码:

struct User: Codable {
    let firstName: String
    let lastName: String
}

struct iExpense: View {
    
    @State private var user = User(firstName: "fang", lastName: "junyu")
    
    func decode(tmp: User) {
        print(JSONEncoder().encode(user))
    }
    
    var body: some View {
        VStack {
            Button("Button"){
                decode(tmp: user)
            }
        }
    }
}

但是上面的print(JSONEncoder().encode(user))会报错:

Call can throw, but it is not marked with 'try' and the error is not handled

这个报错的原因为:JSONEncoder().encode(tmp) 是一个可能会抛出错误的函数。在Swift中,当你调取可能会抛出错误的函数时,必须使用try关键词,并且需要处理可能的错误。

不理解try的人,可能会想,为什么必须使用try关键词,我只想尝试print打印输出这个内容,然后尝试使用三元运算符,当JSONEncoder().encode()为nil时,输出对应的内容:

func decode(tmp: User) {
    JSONEncoder().encode(tmp) ? print(encodedData) : print("Failed to encode user")
}

如果使用三元运算符,会报如下错误:

Cannot convert value of type 'Data' to expected condition type 'Bool'
Cannot find 'encodedData' in scope

这是因为三元运算符不支持错误抛出,因为encode方法可能会抛出错误。

这里可能会存在疑问,那就是很多方法都是可以直接调用的,为什么这里必须使用try关键词来捕获错误。

这里我们需要理解一个Swift的理念:在Swift中,一些方法被设计为“throwing functions(抛出函数)”。这意味着,它们在遇到问题时可以抛出错误,而不是返回正常的结果或者nil。对应这种设计,Swift明确表示该方法可能会抛出错误,并提供错误处理机制,因此,我们必须使用try来捕获可能抛出的错误。

以当前代码为案例,JSONEncoder().encode()可能会抛出如下错误:

  1. 无效的数据类型:当尝试编码的对象不符合Codable协议时,或结构不适合被JSON编码,encode()就会抛出错误。
  2. 编码报错:如果数据结构非常复杂,也可能会在编码过程中出错。
  3. 不兼容数据:可能存在数据类型无法正确转换为JSON格式,例如某些嵌套或不支持的类型。

因此,我们在本案例中,调用抛出函数时,Swift会强制要求你使用try,来调用可能抛出错误的函数。

我们的解决方案为:

func decode(tmp: User) {
    if let encodedData = try? JSONEncoder().encode(tmp) {
        print(encodedData)
    } else {
        print("Failed to encode user")
    }
}

我们给JSONEncoder().encode()方法前面添加一个try?关键词,然后尝试输出这个赋值的内容。

最终的结果为,输出对应字符串的大小:

39 bytes

try和do-catch

当我们已经了解到抛出函数必须使用try?时,我们可以进一步学习一下try和do-catch的三类用法。

用法一:try和do-catch

我们需要了解到当你使用try时,必须使用do-catch来处理可能抛出的错误,当你尝试使用try来调取上面的问题代码时:

func decode(tmp: User) {
    if let encodedData = try JSONEncoder().encode(tmp) {
        print(encodedData)
    } else {
        print("Failed to encode user")
    }
}

你会发现存在这样的报错:

Errors thrown from here are not handled
Initializer for conditional binding must have Optional type, not 'Data'

这是因为,我们虽然使用的是try,但是try必须和do-catch搭配使用,因为Swift要求你明确处理可能抛出的错误,同时try不会自动将错误转换为nil,因此如果函数抛出错误并且没有处理,程序会直接崩溃。

因此,我们应该使用do-catch在try的外面捕获可能抛出的报错并进行处理:

func decode(tmp: User) {
    do {
        let encodedData = try JSONEncoder().encode(tmp)
        print(encodedData)
    } catch {
        print("Failed to encode user")
    }
}

所以,我们的try和do-catch是搭配使用的,当try调取抛出函数并可能抛出错误时,由do-catch捕获问题并进行相关的处理。

用法二:try?

回到我们本次的解决方案,为什么使用try?可行,而try却报错呢?

func decode(tmp: User) {
    if let encodedData = try? JSONEncoder().encode(tmp) {
        print(encodedData)
    } else {
        print("Failed to encode user")
    }
}

这是因为,try?会将抛出的错误结果转换为可选类型,简单的理解为,当try?调取抛出函数时,如果存在错误,try?就会自动将错误转换为nil。

因此,我们可以直接通过print函数调取try函数:

func decode(tmp: User) {
    print(try? JSONEncoder().encode(tmp))
}

当发生报错时,try就会将错误转换为nil。

我们也可以使用上面的if else语句进行判断,当try?遇到错误并转船为nil后,输出else语句。

用法三:try!

当你明确认为不会发生错误时,可以使用try!关键词:

func decode(tmp: User) {
    print(try! JSONEncoder().encode(tmp))
}

当你使用try!时,因为一旦发生错误,程序就会崩溃。所以通常都不推荐使用try!

问题总结

我本打算将本次遇到的问题和try单独分开来写,但感觉单独写一篇try的内容又太少,因此做了一个汇总的问题。

我们先是解决了一个涉及try的报错,又温习或学习了try的三个用法,同时,你也了解到do-catch必须和try组合使用。

总体来说,我之前对应try的学习也很简单,只是知道try可以处理问题,但实际上只有真正的遇到问题并尝试解决时,才会真正的学会如何来运用它们。 全部代码:

import SwiftUI

struct User: Codable {
    let firstName: String
    let lastName: String
}

struct iExpense: View {
    
    @State private var user = User(firstName: "fang", lastName: "junyu")
    
    func decode(tmp: User) {
        if let encodedData = try? JSONEncoder().encode(tmp) {
            print(encodedData)
        } else {
            print("Failed to encode user")
        }
    }
    
    var body: some View {
        VStack {
            Button("Button"){
                decode(tmp: user)
            }
        }
    }
}

#Preview {
    iExpense()
}

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注