5.可选项

76 阅读5分钟

可选项

可选项就是一个变量可以置为nil,在Swift中普通的变量是不能置为nil的,只有可选类型才能将变量置为nil,而且非可选类型的变量初始化的状态也不是nil。可选项其实是相当于对数据重新包装了一层来解决变量为nil的问题,就好像vue3将数据包装一层,来让他有响应式一样。

Optional 是 Swift 中用于表示“有值或无值”的一种强类型系统机制,提升了代码的安全性和可读性。

可选项是对其他类型的一层包装,可以将它理解为一个盒子。如果为nil,那么它是一个空盒子。如果不为nil,那么盒子里面装的是:被包装类型的数据

普通用法

var age:Int? // 默认就是nil
age = 10
age = nil
var array = [1,15,40,29]
func get(_ index:Int) -> Int? {
    if index < 0 || index >= array.count {
        return nil
    }
    return array[index]
}

print("111")
print(get(1))
print(get(-1))
print(get(4))

强制解包

如果要从可选项中取出被包装的数据(将盒子里面的东西取出来),要使用感叹号!进行强制解包

var age2: Int? = 10
var ageInt: Int = age2!

如果对值为nil的可选项(空盒子)进行强制解包,会产生运行时错误

var age3:Int?
age3!

真是因为如果你对可选项强制解包,如果可选项的值为nil,程序就会运行崩溃,所以在强制解包前要判断包内的东西是不是nil,所以强制解包前要先判断。所以如果用强制解包,写法应该按照下面写法。

let number = Int("123")
if number != nil {
    print("字符串转换整数成功:\(number!)")
} else {
    print("字符串转换整数失败")
}

可选项绑定

可选性绑定可以根据可选项绑定来判断可选项是否包含值 如果包含就自动解包,把值赋给一个临时的变量(let)或者变量(var),并且返回true.否则返回false

enum Season : Int {
case spring = 1, summer,autumn,winter
}

if let season = Season(rawValue: 6) {
    switch season {
    case .spring:
        print("the season is spring")
    default:
        print("the season is other")
    }
} else {
    print("no such season")
}

如果我相对两个数据都进行可选性绑定,绑定完再处理,那么我可以使用下面的方式

if let first = Int("4"){
    if let second = Int("42"){
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
        }
    }
}

上面的写法可以简化,简化成下面的样子

if let first = Int("4"),
    let second = Int("42"),
    first < second && second < 100 {
      print("\(second) < \(second) < 100")
    }
if (6 > 5){
    print("hello World")
}

下面是在while循环中的使用

var strs = ["10","20","abc","-20","30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
    sum += num
    index += 1
}
print(sum)

空合并运算符

a ?? b a 是可选项,b是可选项或者不是可选项,b跟a 的存储类型必须相同,如果a不为nil,就返回a,如果a为nil,就返回b。如果b不是可选项,返回a的时候就会自动解包。

如果多个 ??连用,最后的返回值是最后一个类型的参数类型,值是从前往后看的第一个不为nil的值

let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
    print(c)
}

需要注意的是下面的写法,下面的表达式。类似于 if a != nil && b != nil。而不是别的语言的逗号运算符

if let c = a, let d = b {
    print(c)
    print(d)
}

guard语句

guard 条件 else {
  //do someting
  退出当前作用域
 // return, break,continue,throw error
 }
  1. 当guard 语句的false时,就会执行大括号里面的代码 
  2. 当guard语句的条件为true时,就会跳过guard 语句

guard 语句特别适合用来 “提前退出” 使用guard 语句进行可选项绑定时,绑定的常量(let),变量(var)也能在外层作用域中使用

下面举一个不用guard的情况

func login(_ info:[String: String]){
    let username: String
    if let tmp = info["username"] {
        username = tmp
    }else{
        print("请输入用户名")
        return
    }

    let password:String
    if let tmp = info["password"]{
        password = tmp
    }else{
        print("请输入密码")
        return
    }
    print("用户名:\(username)","密码:\(password)","登录ing")
}

如果我使用guard应该用下面的写法

func login1(_ info:[String :String]){
    guard let username = info["username"] else {
        print("请输入用户名")
        return
    }
    guard let password = info["password"] else {
        print("请输入密码")
        return
    }
    print("用户名:\(username)","密码:\(password)","登录ing")
}

隐式解包

  • 在某些情况下,可选项一旦被设定值之后,就会一直拥有值。在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值。可以在类型后面加个感叹号! 定义一个隐式解包的可选项。
  • 隐式可选项可以简单的理解为了不每次使用都去解包,一个是麻烦,另外一个是没有必要采取的一种措施。 下面是隐式解包的具体使用
let num2: Int! = 10
let num3:Int = num2 + 2
print("num3",num3)
// 这里其实就是普通的判断
if num2 != nil {
    // 下面可以直接用是因为隐式解包
    print(num2 + 10)
}
// 这其实是可选绑定
if let num3  = num2 {
    print(num3)
}

可选项的打印问题

可选项在字符串插值或者直接打印时,编译器会发出警告

var age5:Int? = 10
print("My age is\(age5)")

上面的打印会有警告,下面的方式可以消除警告

print("My age is \(age5!)")
print("My age is\(String(describing: age5))")
print("My age is \(age5 ?? 0)")

多重可选项

情况1

var num4: Int? = 10

上面可以简单看做 num4 这个变量里面装了一个盒子,盒子的类型是 Int? 类型,里面的数据是4

情况2

var num5: Int?? = num4

上面可以看做 num5 这个变量里面装了一个盒子,盒子的类型是 Int??类型,盒子里面有套了一个盒子,这个盒子是 Int?类型,然后最里面的盒子里面的放的是 10

情况3

var num6: Int?? = 10
print(num5 == num6) // true

和上面的例子是一样的

情况4 如果里面放的不是10,而是nil,系统怎么处理呢?

image.png