美文网首页
Swift基础-可选链,解包和类型转换

Swift基础-可选链,解包和类型转换

作者: Augs | 来源:发表于2019-12-18 17:04 被阅读0次

    处理丢失的数据

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

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

    Swift的解决方案称为optionals,您可以使任意类型的options。一个可选的整数可能有一个像0或40这样的数字,但它可能根本没有值nil-它可能确实不存在,在Swift中是这样。

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

    var age: Int? = nil
    

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

    age = 38
    

    解开可选项

    可选字符串可能包含“ Hello”之类的字符串,也可能为nil-完全没有。

    考虑以下可选字符串:

    var name: String? = nil
    

    如果使用name.count怎么办?真正的字符串具有一个count属性,该属性存储它具有多少个字母,但这是nil–它是空的内存,不是字符串,因此没有count

    因此,尝试读取name.count是不安全的,Swift不允许这样做。取而代之的是,我们必须查看可选的内容并查看其中的内容-这个过程称为unwrapping

    展开可选内容的一种常见方法是使用if let语法,该语法使用条件进行展开。如果可选变量中有一个值,则可以使用它,但是如果没有,则条件失败。

    例如:

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

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

    if let的替代方法是guard let,它也取消了可选的包装。guard let将为您打开一个可选的选项,但是如果在nil内部找到它,则希望您退出使用它的函数,循环或条件。

    但是,if letguard let之间的主要区别在于,在guard代码之后,未包装的可选内容仍然可用。

    让我们尝试一个greet()函数。这将接受可选字符串作为其唯一参数,并尝试对其进行拆包,但是如果其中没有任何内容,则将打印一条消息并退出。因为可选的选项guard letguard完成后会留在原处,所以我们可以在函数末尾打印未封装的字符串:

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

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

    强制展开

    可选参数表示可能存在或可能不存在的数据,但是有时您可以确定一个值不是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某些东西无法转换为整数-您的代码将崩溃。

    结果,只有在确定它是安全的时才应强制打开包装–这是通常将其称为崩溃操作符的原因。

    隐式展开的可选内容

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

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

    let age: Int! = nil
    

    因为它们的行为就好像它们已经被解开,所以您不需要if letguard let使用隐式解开的可选对象。但是,如果您尝试使用它们,而它们没有任何价值nil(如果确实如此),则您的代码将崩溃。

    隐式展开的选项存在,因为有时变量将以nil开始,但在需要使用它之前将始终有一个值。因为你知道它们在你需要的时候会有价值,所以不必一直写if let是有帮助的。

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

    Nil合并

    nil合并运算符解开一个可选值,如果有则返回内部的值。如果没有值(如果可选为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()函数返回的结果:如果是字符串,则将其解包并放入user,但是如果它内部为nil,则将改用“Anonymous”。

    可选链接

    Swift在使用可选选项时为我们提供了一条捷径:如果您想访问诸如a.b.c其中b是可选项,则可以在其后写一个问号以启用可选链接:a.b?.c

    运行该代码时,Swift将检查b是否具有值,如果该值是nil,则该行的其余部分将被忽略-Swift将立即返回nil。但是,如果它具有值,它将被解包并继续执行。

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

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

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

    let beatle = names.first?.uppercased()
    

    这个问号是可选的链接-如果first返回nil则Swift不会尝试将其大写,而是立即设置beatlenil

    可选try

    回到我们谈论抛出函数的时候,我们看了下面的代码:

    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.")
    }
    

    运行投掷功能,使用dotry以及catch优雅地处理错误。

    try有两种替代方法,既然您了解可选方法并强制展开,则这两种方法都会更有意义。

    第一个是try?,并将throwing函数更改为返回可选函数的函数。如果函数抛出错误nil,则将结果发送给您,否则,您将获得返回值,并将其包装为可选值。

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

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

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

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

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

    初始化失败

    在谈论强制展开时,我使用了以下代码:

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

    它将字符串转换为整数,但是由于您可能尝试传递任何字符串,因此您实际上返回的是一个可选整数。

    这是一个失败的初始化程序:可能有效或无效的初始化程序。您可以使用init?()而不是init(),在自己的结构和类中编写这些代码,并在出现问题时返回nil。然后,返回值将是您的类型的可选值,以便您随意展开。

    例如,我们可以编写一个Person必须使用9个字母的ID字符串创建的结构。如果使用了除9个字母的字符串以外的其他任何字符,我们将返回nil,否则我们将照常继续。

    这是Swift中的内容:

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

    类型转换

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

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

    我们可以创建几条鱼和几条狗,并将它们放入数组中,如下所示:

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

    Swift可以看到FishDog两者从Animal类继承,因此它使用类型推断Animal来构成一个pets数组。

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

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

    这是我们在Swift中编写循环的方式:

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

    总结

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

    相关文章

      网友评论

          本文标题:Swift基础-可选链,解包和类型转换

          本文链接:https://www.haomeiwen.com/subject/rcyunctx.html