Swift循环语句对可选类型进行解包
Swift循环语句对可选类型进行解包

Swift循环语句对可选类型进行解包

当我尝试在《汇率仓库》应用中,配置一个收藏汇率数组:

struct UpdateER {
    // 全部的汇率信息
    var updateERInfo: ForexDataStruct?
    // 返回用户设置的五个固定汇率
    var favoriteER: [ForexDataStruct_Info] = []
mutating func getFavoriteER() {
    ForEach(0..<5) { index in
        favoriteER.append(updateERInfo?.records[index])
    }
}
    ...
}

这个代码中,updateERInfo是整个汇率数组结构,我想要从这个结构中分离出各个国家的汇率信息,以便后面可以单独设置收藏的汇率信息。

但是,目前这个代码的favoriteER.append()报错了,报错输出为:

Cannot convert value of type 'ForexDataStruct_Info?' to expected argument type 'ForexDataStruct_Info'
Insert '!'
No exact matches in reference to static method 'buildExpression'

第一个报错

Cannot convert value of type 'ForexDataStruct_Info?' to expected argument type 'ForexDataStruct_Info'

表示updateERInfo?.records[index] 返回的是一个可选类型 (ForexDataStruct_Info?),而 favoriteER 数组只接受非可选类型 (ForexDataStruct_Info)。

因此,应该在将整个汇率数据中各个国家的汇率信息updateERInfo?.records[index]追加到favoriteER数组之前,先解包可选类型。例如,使用if、guard等形式,确保追加的值不是nil。

guard let records = updateERInfo?.records else {
    print("无法获取汇率数据")
    return
}

第二个报错

Insert '!'

表示可以使用”!”来强制解包可选类型,但是确定updateERInfo 和 records 中的元素都不为 nil,则可以使用 ! 强制解包。

否则会因为nil值导致运行报错。

第三个报错

No exact matches in reference to static method 'buildExpression'

这个报错表示在非SwiftUI环境中使用ForEach导致的,ForEach主要用于在SwiftUI构建视图,编译器无法将ForEach用于普通循环。

应该使用for循环替代ForEach。

for index in 0..<5 {}

最后实现的代码为:

struct UpdateER {
    // 全部的汇率信息
    var updateERInfo: ForexDataStruct?
    // 返回用户设置的五个固定汇率
    var favoriteER: [ForexDataStruct_Info] = []

    mutating func getFavoriteER() {
        
        for index in 0..<5 {
            guard let record = updateERInfo?.records[index] else {
                    print("无法获取汇率信息")
                    return
                }
            favoriteER.append(record)
        }
    }
    ...
}

问题总结

本次主要涉及的是两个知识点。

解包

首先是Swift中的解包。我们在处理第一个报错时,因为我们的updateERInfo?.records返回的是一个可选类型ForexDataStruct_Info?,这意味着它的返回值可能是nil。

updateERInfo?.records

当遇到返回值是nil时,如果不去处理它就会报错。

因此,为了安全的使用这个可选值,必须要先进行解包,将可选类型转为必选类型。这里,我们采用的是guard let进行的解包。

guard let record = updateERInfo?.records[index] else {
    print("无法获取汇率信息")
    return
}

在这段代码中,guard let会尝试将updateERInfo?.records的值解包,如果updateERInfo.records有值,那么records就能够被解包为非可选类型,并赋值给records。

如果updateERInfo.records为nil,则guard语句提前返回,且后续代码不会运行。

这里可能会让人感到不解的是,record明明只是一个赋值,将updateERInfo?.records[index]赋给record,为什么就变成解包了。

我们可以假设两种场景:

一种是updateERInfo?.records[]为[1,2,3,4,5],另一种场景的updateERInfo?.records为nil。

第一种场景中,我们通过for循环进行遍历,所以实际的代码展示为:

updateERInfo?.records[0]    // record = 1
updateERInfo?.records[1]    // record = 2
updateERInfo?.records[2]    // record = 3
updateERInfo?.records[3]    // record = 4
updateERInfo?.records[4]    // record = 5

在这里,我们的record都是具体的值,这也是解包的意思。

第二种场景中,因为特殊原因,比如没有完成初始化或赋值等操作,这是我们的updateERInfo?.records是nil。

updateERInfo?.records[1]    // record解包失败,退出整个for循环
updateERInfo?.records[2]    // 不再执行
updateERInfo?.records[3]    // 不再执行
updateERInfo?.records[4]    // 不再执行
updateERInfo?.records[5]    // 不再执行

通过这里,我们可以看到,解包实际上就是完成一个赋值的操作,让被赋值的变量明确为不是nil,当updateERInfo?.records数组为nil时,将退出整个for循环,不再继续执行。

此外,还有一种数组的解包情况,那就是数组是一个可选类型数组,比如:

var favoriteER: [ForexDataStruct_Info?] = []

在这里,数组中可能存在nil,处理可选类型的数组中的nil,就需要考虑将前面的return改为continue。

for index in 0..<5 {
    // 这里 record 是一个可选类型,可能为 nil
    if let record = updateERInfo?.records[index] {
        favoriteER.append(record) // 只有当 record 不为 nil 时才追加到 favoriteER 中
    } else {
        print("无法获取汇率信息,index: \(index)")
        continue // 跳过此循环迭代,继续下一个
    }
}

当数组中某一个值为nil,则使用continue跳过当前nil值,继续处理下一个值。

var favoriteER: [ForexDataStruct_Info?] = []

这里的两个区别就是,如果单纯判断数组是否为nil,使用guard let判断,如果失败则直接return,这将结束整个循环和所在的函数/方法。

var favoriteER: [ForexDataStruct_Info] = []

当数组中值存在nil时,我们就可以使用if else语句来判断,如果遇到nil值,则通过continue跳出并处理下一个值。

ForEach语句

我们第三个错误是因为在非SwiftUI环境中使用ForEach导致的,ForEach主要用于在SwiftUI构建视图。

因此,后续在循环时应注意,非视图结构使用普通的for-in循环,视图结构使用ForEach语句。

以上是本文的全部内容。

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

发表回复

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