Swift5.x入门08--可选项Optional

可选项

  • 可选项,一般也叫可选项类型,它允许将值设置为nil;
  • 在类型名称后面加上?,来定义一个可选项;

强制解包

  • 可选项是对其他类型的一层包装,可以将它理解为一个盒子;
  • 如果可选项的值为nil,那么其是一个空盒子;
  • 如果可选项的值不为nil,那么盒子中装的是被包装类型的数据;
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //默认值为nil
        var age: Int?
        age = 10 //age可以存储Int数据
        age = nil //age可以存储nil
    }
}
  • 定义一个可选项类型age,没有初始化,则默认值为nil;
  • age可以存储Int数据,也可以存储nil;
  • 如果要从可选项中取出被包装的数据,需要使用感叹号!,进行强制解包;
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //默认值为nil
        var age: Int? = 30
        age = 40
        let count: Int = age!
        print(count)
    }
}
  • 如果对值为空nil的可选项进行 强制解包,将会产生运行时错误

判断可选项是否包含值--使用if -- else

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //num是可选项类型
        let num = Int("123")
        if num != nil{
            print("字符串转换成功 \(num!)")
        }else{
            print("字符串转换失败")
        }
    }
}
  • Int("123")将字符串转换成整数,有可能转换失败返回nil,所以其返回值是一个可选项类型,在其不为空的情况下才能强制解包;

判断可选项是否包含值--使用switch -- case

func test() -> Void {
    let num = Int("123")
    switch num {
    case let v?:
        print("num有值",v)
    default:
        print("num为nil")
    }
}
  • case let v?:表示可选项num若不为nil,会自动解包将被包装的值赋值给常量v,所以v是Int类型;

自动解析

image.png
  • 在定义可选项时,直接使用! 替代 ?,那么在解包使用数据时,可以省略!
var myString:String!

myString = "Hello, Swift!"

if myString != nil {
   print(myString)
}else{
   print("myString 值为 nil")
}

可选项绑定

  • 使用可选项绑定来判断可选项是否包含值;
  • 如果包含值就自动解包,把值赋值给一个临时的常量let或者变量var,并返回true;
  • 否则直接返回false;
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let num = Int("123") {
            print("字符串转换成功 \(num)")
        }else{
            print("字符串转换失败")
        }
    }
}
import UIKit

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

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let season = Season(rawValue: 6) {
            switch season {
            case .spring:
                print("春")
            default:
                print("其他")
            }
        }else{
            print("枚举不存在")
        }
    }
}
  • Season(rawValue: 6)其返回值也是可选项类型,所以可以使用可选项绑定;

等价写法

import UIKit

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

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let first = Int("123") {
            if let second = Int("234") {
                if first < second {
                    print("\(first) -- \(second)")
                }
            }
        }
        //逗号隔开
        if let first = Int("123"),
           let second = Int("234"),
           first < second {
            print("\(first) -- \(second)")
        }
    }
}

while循环中使用可选项绑定

import UIKit
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let 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)
    }
}
  • Int(strs[index])是可选项类型,则使用可选项绑定,且满足是正数,才能进行相加操作;

空合并运算符??

  • a ?? b
  • a是可选项类型;
  • b是可选项,也可以不是可选项类型;
  • a和b的存储类型必须相同;
  • 如果a不为nil,就返回a;
  • 如果a为nil,就返回b;
  • 如果b不是可选项,返回a是就是自动解包;
import UIKit

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

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 1
        let b: Int? = 2
        let c = a ?? b
        print(c) //Int? Optional(1)
        
        let a: Int? = nil
        let b: Int? = 2
        let c = a ?? b
        print(c) //Int? Optional(2)
        
        let a: Int? = nil
        let b: Int? = nil
        let c = a ?? b
        print(c) //Int? nil
        
        let a: Int? = 1
        let b: Int = 2
        let c = a ?? b
        print(c) //Int 1
    }
}
  • 根据上面??的用法规则,可以确定常量c的数据类型及数值;

多个空合并运算符?? 一起使用

import UIKit

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

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 1
        let b: Int? = 2
        let c = a ?? b ?? 3 //c Int 1
        print(c)
    }
}
  • 从左到右依次运算,与单个??运算类似;

??与if let的配合使用

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        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)
        }
        //等价于if(a != nil && b != nil)
    }
}

guard

guard 条件 else {
   退出当前作用域 
}
  • 当前guard条件为false时,就会执行大括号中的代码;
  • 当前guard条件为true时,就会跳过大括号中的代码;
  • guard语句与if条件语句,功能相反;
  • 当使用guard语句进行可选项绑定时,绑定的常量,变量也能在外层作用域中使用;
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        login(["userName":"liyanyan","password":"123456"])
    }
    
    func login(_ info: [String : String]) -> Void {
        guard let userName = info["userName"] else {
            print("请输入用户名")
            return
        }
        guard let password = info["password"] else {
            print("请输入密码")
            return
        }
        print("userName = \(userName) -- password = \(password)")
    }
}
  • guard语句特别适合用来做提前退出,即条件不成立的逻辑;

隐式解包

  • 在某些情况下,可选项一旦被设定值之后,就一定会拥有值;
  • 在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值;
  • 可以在类型的后面加一个感叹号!,定义一个隐式解包的可选项;
let num: Int! = 10
  • num是可选项类型,但已经隐式解包了,在使用的时候不用再强制解包;

字符串插值

  • 可选项在字符串插值或直接打印的时候,编译器会发出警告;
override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 10
        print(a)
        print("a = \(a)")
}
  • Expression implicitly coerced from 'Int?' to 'Any'
  • String interpolation produces a debug description for an optional value; did you mean to make this explicit?
  • 消除警告
override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 10
        print(a!)
        print("a = \(a!)")
}
  • 使用强制解包,消除警告;
override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 10
        print(a!)
        print("a = \(a!)")
        print("a = \(String(describing: a))")
}
  • 调用String方法,获取字符串,消除警告;
override func viewDidLoad() {
        super.viewDidLoad()
        
        let a: Int? = 10
        print(a!)
        print("a = \(a!)")
        print("a = \(String(describing: a))")
        print("a = \(a ?? 0)")
}
  • 使用空合并运算符 ?? 可选项a会自动解包;

多重可选项

override func viewDidLoad() {
        super.viewDidLoad()
        
        let num: Int? = 10
        let num1: Int?? = num
        let num2: Int?? = 10
    }
  • 可选项的原理图如下所示:
Snip20210729_47.png
  • 可选项num类型为Int?,盒子内部包装了数据10;
  • 可选项num1类型为Int??,盒子内部包装了数据Int?,层层包装;
  • 可选项num2与num1等价;
override func viewDidLoad() {
        super.viewDidLoad()
        
        let num: Int? = nil
        let num1: Int?? = num
        let num2: Int?? = nil
        
        (num1 ?? 1) ?? 2 //2
        (num2 ?? 1) ?? 2 //1
}
  • 原理图如下所示:
Snip20210729_49.png
  • num1与num2不是等价的,这与前面的有所不同;
  • 可以通过LLDB指令 fr v -R 可选项 查看数据结构;
案例应用
  • 代码如下所示:
import Foundation

extension String {
    func dateFormatterWithDateString(dateString: String?) -> String {
        guard let m_dateString = dateString else { return "" }
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
        dateFormatter.timeZone = TimeZone.current
        
        let date = dateFormatter.date(from: m_dateString)
        //date是可选项类型 使用时:
        //可直接使用可选项
        //也可解包
        // 1.强制解包
        // 2.可选项绑定 解包

        //可强制解包 不推荐若seconds为nil时,会造成runtime崩溃
        if seconds! < 0.0 {
            
        }
        //可选项绑定 解包
        guard let s = seconds,s < 0.0 else { return "" }
        
        return "sss"
    }
}
  • date在使用时可以直接使用可选项date?,那么与其相关联的seconds也是一个可选项 let seconds = date.timeIntervalSinceNow ,seconds在参与运算时必须解包,可强制解包可选项绑定解包
import Foundation

extension String {
    func dateFormatterWithDateString(dateString: String?) -> String {
        guard let m_dateString = dateString else { return "" }
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:SS"
        dateFormatter.timeZone = TimeZone.current
        
        let date = dateFormatter.date(from: m_dateString)
        //date是可选项类型 使用时:
        //可直接使用可选项
        //也可解包
        // 1.强制解包
        // 2.可选项绑定 解包

        let seconds = date!.timeIntervalSinceNow
        print(seconds + 10)
        //会报错
        if let ss = seconds {
            print(seconds + 10)
        }
        return "sss"
    }
}
  • let seconds = date!.timeIntervalSinceNow,date强制解包了,那么seconds就是确定的值,所以直接使用即可,无需解包,解包会报错;

你可能感兴趣的:(Swift5.x入门08--可选项Optional)