100 Days of SwiftUI —— Day 12:可选类型

100 Days of SwiftUI —— Day 12:可选类型_第1张图片

空(Null)引用——从字面上讲是一个无值的变量——是托尼·霍尔(Tony Hoare)于1965年发明的。回想起来,他说:“我称之为十亿美元的错误(I call it my billion-dollar mistake)”,因为它们会导致很多问题。

这是您学习Swift基础知识的最后一天,它专门用于Swift解决null引用(称为可选类型)的解决方案。这些是一项非常重要的语言功能,但会给您的大脑带来一点伤害——如果您需要重复播放一些视频,那也不要感到难过。

本质上,可选值试图回答以下问题:“如果我们的变量没有值该怎么办?”Swift希望确保我们所有的程序都尽可能安全,因此它具有一些非常具体且非常重要的功能!——处理这种情况的技术。

今天,您有11个一分钟的视频可供观看,并且您将看到解包可选链类型转换等更多内容。观看完每个视频后,我们会进行一次简短的测试,以帮助您了解所教的内容。

1. 处理丢失的数据 Handling missing data – test

我们使用了Int之类的类型来保存5之类的值。但是,如果您想为用户存储年龄属性,如果您不知道某人的年龄,该怎么办?

您可能会说“好吧,我会存储0”,但这样会使新生儿和您不知道年龄的人混淆。您可以使用一个特殊的数字(例如1000或-1)来表示“未知”,这两个年龄都是不可能的年龄,但是您真的会在所有使用过的地方记住该数字吗?

Swift的解决方案称为“可选(optionals)”,您可以将任意类型设置为可选。一个可选的整数可能有一个像0或40这样的数字,但它可能根本没有值——它可能确实不存在,在Swift中为nil

要使类型为可选,请在其后添加问号。例如,我们可以这样创建一个可选的整数:

var age: Int? = nil

那没有任何数字——它没有存储任何东西。但是,如果我们以后知道那个年龄,我们可以使用它:

age = 38

2. 解包 Unwrapping optionals – test

可选字符串可能包含诸如“ Hello”之类的字符串,也可能为nil——没有值。

思考以下可选字符串:

var name: String? = nil

如果我们使用name.count会怎样?真实的字符串具有一个count属性,该属性存储它拥有多少个字母,但这是nil——它是空的内存,不是字符串,因此没有count

因此,尝试读取name.count是不安全的,Swift不允许这样做。相应的,我们必须查看可选值的内容——这个过程称为解包。

解包可选对象的一种常见方法是使用if let语法,它会与条件一起解包。如果可选内容中包含一个值,则可以使用它,但如果没有,则条件失败。

例如:

if let unwrapped = name {
    print("\(unwrapped.count) letters")
} else {
    print("Missing name.")
}

如果name包含一个字符串,它将作为常规String放入unwrapped,我们可以在条件内读取其count属性。或者,如果name为空,则将运行else代码。

3. 使用guard解包 Unwrapping with guard – test

使用if let解包的替代方案是guard let, guard let将为您解开一个可选的选项,但是如果在其中找到nil,则希望您退出使用它的函数,循环或条件。

总的来说,if letguard let之间的主要区别在于,在guard之后,解包后的内容在guard代码仍然可用。(通俗的说:if let的解包值在大括号内使用,guard let的解包值,在大括号外的下文中使用)

让我们尝试使用greet()函数。这将接受一个可选字符串作为唯一参数,并尝试对其进行解包,但是如果其中没有任何内容,则会显示一条消息并退出。由于使用guard let解包的可选选项在guard代码完成后仍会保留,因此我们可以在函数末尾打印解包的字符串:

func greet(_ name: String?) {
    guard let unwrapped = name else {
        print("You didn't provide a name!")
        return
    }

    print("Hello, \(unwrapped)!")
}

使用guard let可让您在功能开始时处理问题,然后立即退出。这意味着函数的其余部分是幸福的路径——如果一切正确,代码将采用的路径。

4. 强制解包 Force unwrapping – test

可选参数代表可能存在或可能不存在的数据,但有时您可以肯定地知道该值不是nil。在这些情况下,Swift可让您强制解包:将其从可选类型转换为非可选类型。

例如,如果您有一个包含数字的字符串,则可以将其转换为如下所示的Int

let str = "5"
let num = Int(str)

这使num成为可选的Int,因为您可能已尝试转换“Fish”而不是“5”之类的字符串。即使Swift不确定转换是否能正常进行,您也可以看到代码是安全的,因此可以通过在Int(str)之后编写来强制打开结果,像这样:

let num = Int(str)!

Swift将立即打开可选包装,并将num设置为常规Int而不是Int?。但是如果您错了——如果`str·是无法转换为整数的内容——您的代码将崩溃。

5. 隐式解包 Implicitly unwrapped optionals – test

像常规的可选内容一样,隐式解包的可选内容可能包含一个值,或者它们可能为nil。但是,与常规的可选选项不同,您无需解开它们即可使用它们:您可以像完全不是可选的那样使用它们。

通过在您的类型名称后添加感叹号来创建隐式解包的可选内容,如下所示:

let age: Int! = nil

由于它们的行为就好像它们已经被解包一样,因此不需对隐式解包的可选对象使用if letguard let。但是,如果您尝试使用它们而没有任何值——如果它们为nil——您的代码将崩溃。

存在隐式解包的可选参数,因为有时变量将以nil开始,但在需要使用它之前始终具有值。因为您知道它们会在您需要它们的时候就具有价,所以不必一直if let,这很有用。

话虽这么说,但是如果您能够使用常规的可选值,通常是个好主意。

6. 空合运算 Nil coalescing – test

空合运算符展开一个可选的,如果有,则返回其中的值。如果没有值(如果可选值为nil),则使用默认值。不管是哪种方式,结果都不是可选的:它要么是由可选的内部值决定的,要么是用备份的默认值。

下面是一个函数,它接受一个整数作为它的唯一参数并返回一个可选字符串:

func username(for id: Int) -> String? {
    if id == 1 {
        return "Taylor Swift"
    } else {
        return nil
    }
}

如果我们使用ID 15调用它,我们将返回nil,因为无法识别用户,但是使用空合运算,我们可以提供一个默认值“Anonymous”,如下所示:

let user = username(for: 15) ?? "Anonymous"

这将检查从username()函数返回的结果:如果它是一个字符串,那么它将被展开并放入用户中,但是如果它的内部有nil,那么将使用“Anonymous”。

7. 可选链 Optional chaining – test

Swift在使用optionals时为我们提供了一个快捷方式:如果您想访问a.b.c但是b是可选的,您可以在它后面写一个问号以启用可选链:a.b?.c

当代码运行时,Swift将检查b是否有一个值,如果是nil,则该行的其余部分将被忽略——Swift将立即返回nil。但是,如果它有一个值,它将被展开并继续执行。

要尝试此操作,请使用以下名称数组:

let names = ["John", "Paul", "George", "Ringo"]

我们将使用该数组的第一个属性,如果有一个,则返回第一个名称;如果数组为空,则返回nil。然后,我们可以对结果调用uppercased()使其成为大写字符串:

let beatle = names.first?.uppercased()

这个问号是可选链,如果首先返回nil,那么Swift不会尝试大写,并会立即将beatle设置为nil

8. 可选try Optional try – test

回到在我们讨论抛出函数时,我们看到了以下代码:

enum PasswordError: Error {
    case obvious
}

func checkPassword(_ password: String) throws -> Bool {
    if password == "password" {
        throw PasswordError.obvious
    }

    return true
}

do {
    try checkPassword("password")
    print("That password is good!")
} catch {
    print("You can't use that password.")
}

它运行一个抛出函数,使用dotrycatch来优雅地处理错误。

有其他两种方法来写try,这两种方法都会更有意义,因为你已经了解了可选值和强制解包。

首先是try?,并将抛出函数更改为返回可选函数的函数。如果函数抛出一个错误,则结果为nil,否则返回值将被包装为可选值。

使用try?我们可以这样运行checkPassword()

if let result = try? checkPassword("password") {
    print("Result was \(result)")
} else {
    print("D'oh.")
}

另一个选择是try!,在确定函数不会失败时可以使用。如果函数确实抛出错误,则代码将崩溃。

使用try!我们可以将代码重写为:

try! checkPassword("sekrit")
print("OK!")

9. 可失败初始化器 Failable initializers – test

在谈到强制解包时,我使用了以下代码:

let str = "5"
let num = Int(str)

它将字符串转换为整数,但因为您可能会尝试在那里传递任何字符串,所以实际上得到的是一个可选整数。

这是一个可失败的初始值设定项:一个可能工作也可能不工作的初始值设定项。您可以在自己的结构和类使用中init?编写他们,而不是init(),如果出现问题则返回nil。然后,返回值将是您的类型中的一个可选值,供您根据需要展开。

例如,我们可以编写一个Person结构,该结构必须使用九个字母的ID字符串创建。如果使用的不是9个字母的字符串,我们将返回nil,否则我们将继续正常工作。

struct Person {
    var id: String

    init?(id: String) {
        if id.count == 9 {
            self.id = id
        } else {
            return nil
        }
    }
}

10. 类型转换 Typecasting – test

Swift必须始终知道每个变量的类型,但有时你知道的信息比Swift多。例如,这里有三个类:

class Animal { }
class Fish: Animal { }

class Dog: Animal {
    func makeNoise() {
        print("Woof!")
    }
}

我们可以创建两条鱼和两只狗,然后将它们放入一个数组中,如下所示:

let pets = [Fish(), Dog(), Fish(), Dog()]

Swift可以看到FishDog都继承自Animal,因此它使用类型推断使pets成为一个Animal数组。

如果我们想在pets数组上循环并要求所有的狗吠叫,我们需要执行一个类型转换:Swift将检查每个宠物是否是一个Dog对象,如果是,我们可以调用makeNoise()

这使用了一个新的关键字as?,它返回一个可选值:如果类型转换失败,则返回nil,否则返回转换后的类型。

下面是我们如何用Swift编写循环:

for pet in pets {
    if let dog = pet as? Dog {
        dog.makeNoise()
    }
}

关于类型转换的其他更好的写法可参考:Swift编程小技巧中的相关内容。

11. 可选值:总结 Optionals summary – test

您已经完成了本系列的第十部分,所以让我们总结一下:

1、可选类型让我们以一种清晰明确的方式来表示一个值的缺失。
2、Swift不会让我们在没有解包的情况下使用可选值,无论是使用if let还是使用guard let
3、可以使用感叹号强制解包,但如果尝试强制展开nil,则代码将崩溃。
4、隐式解包没有常规可选值的安全检查。
5、您可以使用空合运算符(nil coalescing)??来展开一个可选值,如果里面没有任何内容,则提供一个默认值。
6、可选链允许我们编写代码来操作可选值,但是如果可选值的结果是空,则忽略代码,并返回nil
7、你可以使用try?要将抛出函数转换为可选返值,或者使用try!,但是在抛出错误时会崩溃。
8、如果您需要初始化器在输入错误时失败,请使用init?设置可失败初始化器。
9、可以使用类型转换将一种对象类型转换为另一种对象类型。

赏我一个赞吧~~~

你可能感兴趣的:(100 Days of SwiftUI —— Day 12:可选类型)