Swift笔记5:可选项(Optional)、可选链(Optional Chaining)

内容分享2小时前发布
0 0 0

可选项(Optional)

一些定义和解包练习

     func execOptional() {
        // 定义
        let a: String? = "A"
        print(type(of: a)) // Optional<String>
        let b: String? = "B"
        print(type(of: b)) // Optional<String>
        // 强制转换可能为nil,所哟是optional类型
        let m = Int("a")
        print(type(of: m)) // Optional<Int>
        // 可选类型本身是个枚举,有值或者为空
        let c: Int? = Optional.some(17)
        let d: Int? = Optional.none
        print(c ?? -1) // 17
        print(d ?? -1) // -1
        // 强制解包!
        // 表达式判断解包if let temp = c
        // 表达式判断解包guard let temp = d
        // ??解包
        if let temp = c {
            print("c不为空", "c的值是:(temp)") // d为空 d的值是:17
        }
        guard let temp = d else {
            print("d为空,但是d的值拿不到") // d为空 d的值是:17
            return
        }
        var string: String? = "Position"
        // string?
        print(string?.hasPrefix("P")) // Optional(true)
        if let temp = string?.hasPrefix("P") {
            print(temp) // true 传入Optional() 自动解包
        }
        string = nil
        print(string ?? -999) // -999
    }

Optional类型的map和flatMap

如果是可选类型就可以使用map和flatMap

var num1: Int? = 10
var num2 = num1.map { $0 * 2 } // Optional(20)
var num3: Int? = nil
var num4 = num3.map { $0 * 2 } // nil

// 注意flatMap发现已经是Optional类型了就不会再包装一层了
var numA: Int? = 10
var numB = numA.map { Optional.some($0 * 2) } // Optional(Optional(20))
var numC = numA.flatMap { Optional.some($0 * 2) } // Optional(20)

Optional类型的map和flatMap具体用途

下面几个例子就是我们想对一个Optional进行判断如果有值,就先对Optional解包进行闭包处理,如果为nil就不处理,如果有值的话返回处理后的Optional。闭包处理很灵活,也可以是函数。
$0 是传入解包后的值

var num11: Int? = 10
var num22 = (num1 != nil) ? (num1! + 10) : nil
var num33 = num1.map { $0 + 10 } // num22、num33是等价的

var fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var str: String? = "2011-09-10"
var date1 = str != nil ? fmt.date(from: str!) : nil
var date2 = str.flatMap { fmt.date(from: $0) } // Optional(2011-09-09 16:00:00 +0000)

struct Person {
    var name: String
    var age: Int
}

var items = [
    Person(name: "jack", age: 20),
    Person(name: "rose", age: 21),
    Person(name: "kate", age: 22)
]

func getPerson2(_ name: String) -> Person? {
    return items.firstIndex { $0.name == name }.map { items[$0] }
}

struct Person {
    var name: String
    var age: Int
    init?(_ json: [String: Any]) {
        guard let name = json["name"] as? String,
              let age = json["age"] as? Int
        else {
            return nil
        }
        self.name = name
        self.age = age
    }
}
// old
var p1 = json != nil ? Person(json!) : nil
// new
var p2 = json.flatMap(Person.init)

可选链(Optional Chaining)

可选链描述的是一个对象是可选类型,它所包含的属性、方法下标也需要解包后处理。以及后续的属性变成可选项。
如下面的代码本身不是可选项的dog,weight,price也变成了可选项
var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int?
var price = person?.car?.price // Int?
多个?可以链接在一起
如果链中任何一个节点是nil,那么整个链就会调用失败

class Car { var price = 0 }
class Dog { var weight = 0 }
class Person {
    var name: String = "jack"
    var dog: Dog = .init()
    var car: Car? = Car()
    func age() -> Int { 18 }
    func eat() { print("Person eat") }
    subscript(index: Int) -> Int { index }
}
var person: Person? = Person()
var age1 = person!.age() // Int
var age2 = person?.age() // Int?
var name = person?.name // String?
var index = person?[6] // Int?
print(person?.name) // Optional("jack")
print(type(of: person?.name)) // Optional<String>
if let temp = person {
    print(temp.name) // jack
}
var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int?
var price = person?.car?.price // Int?

如果可选项为nil,调用方法、下标、属性失败,结果为nil
如果可选项不为nil,调用方法、下标、属性成功,结果会被包装成可选项
如果结果本来就是可选项,不会进行再次包装

OC里面可以给空对象发送消息,Swift中空对象是不会调用方法的。
下例中不会调用getName()

func getName() -> String {
    "jack"  //person为nil的时候不会执行改方法
}
var person: Person? = Person()
person = nil
person?.name = getName()

字典?的应用

var scores = ["Jack": [86, 82, 84],
              "Rose": [79, 94, 81]]
scores["Jack"]?[0] = 100
let _:Int? = scores["Jack"]?[0] //Optional(100)
scores["Rose"]?[2] += 10
let _ = scores["Rose"]?[2] //Optional(91)
scores["Kate"]?[0] = 88

下例子num1和num1?的区别
使用num1?的时候先判断num1是不是nil,如果为空就不进行后面的赋值操作。
使用num1的时候直接赋值

var num1: Int? = 5
num1 = 7 //Optional(7)
num1? = 10 //Optional(10)
print(num1)
var num2: Int? = nil
num2? = 70
print(num2) //nil
num2 = 70   // nil
print(num2) //Optional(70)

可选项的本质

可选项的本质是enum类型
下面代码中Optional是个枚举,Wrapped既是泛型,也是关联值。

新的API
@frozen public enum Optional<Wrapped> : ~Copyable where Wrapped : ~Copyable {
    case none
    case some(Wrapped)
    public init(_ some: consuming Wrapped)
}
老的API
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    public init(_ some: Wrapped))
}
func execGenerics() {
    var age1: Int? = 10
    var age2:Optional<Int> = 10
}

下面的age是等效的。互为语法糖

var age1: Int? = 10
var age2:Optional<Int> = 20
var age3:Optional<Int> = Optional.some(30)
var age4:Optional<Int> = .some(40)
var age5:Int? = .none
var age6:Int? = .some(50)
var age7:Int? = nil
var age8 = Optional<Int>.none
var age9 = Optional<Int>.some(70)

可选类型switch
下面的case let v?:中 ,相当于num = v 都是optional,不像if let 一样会自动解包。

let num: Int? = 30
switch num {
case let v?:
    print(v) // 30
case nil:
    print(num ?? 0)
}

let num1: Int? = nil
switch num1 {
case let .some(v):
    print("some", v)
case .none:
    print("none") // none
}

多重可选项

var age_: Int? = 10
var age: Int?? = age_
age = nil

var age0 = Optional.some(Optional.some(10))
age0 = .none
var age1: Optional<Optional> = .some(.some(10))
age1 = .none

var age2: Int?? = 10
var age3: Optional<Optional> = 10

© 版权声明

相关文章

暂无评论

none
暂无评论...