当我尝试在《汇率仓库》应用中,配置一个收藏汇率数组:
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语句。
以上是本文的全部内容。