百日学 Swift(Day 12) – 可选项、解包和类型转换

百日学 Swift(Day 12) – optionals, unwrapping, and typecasting(可选项、解包和类型转换)

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

其实应该翻译成处理未知数值比较贴切。当不能确定存储属性的值时,Swift 提供了可选项的方式,表示该属性可能为nil(空)。要使类型可选,在其后加上?即可。

var age: Int? = nil

2. Unwrapping optionals(可选项的解包)test

var number: Int?

由于可选项可能为 nil,所以在使用的时候要先确保其有值,因为 nil 在绝大多数时候是不能参与运算的。

number = 2
print(number? * 10)

这样会报错:Value of optional type 'Int?' must be unwrapped to a value of type 'Int'

使用!可以对可选项强行解包,但是前提是可选项不能是 nil。如果 number 是 nil 还是会报错的。

print(number! * 10)

可以用??加上默认值的方法避免空值。但这种方法有局限性。

print((a ?? 0) + 2)	// 如果 a 是 nil,就取默认值 0

使用if let方式可以较好地处理。

if let unwrapped = number {
    print("\(unwrapped * 10)")	// number 不为 nil 时
} else
    print("Missing number.")	// unwrapped 为 nil,即 number 为 nil 时
}

3. Unwrapping with guard(使用 guard 解包)test

if let的替代方法是guard let,具体用法是在guard let后面直接写else和处理无值情况的闭包,如果有值则继续执行后面的语句。

func greet(_ name: String?) {
    guard let unwrapped = name else {		// 处理空值,注意 else
        print("You didn't provide a name!")
        return
    }

    print("Hello, \(unwrapped)!")	// 注意,这里不再使用 name,而是要用保证有值的 unwrapped
}

4. Force unwrapping(强制解包)test

使用!可以对可选项强行解包,但是可能会因为出问题。比如,如果尝试将字符串转换为整数,会因为转换失败造成程序崩溃。!通常被称为“崩溃操作符”。程序员必须对程序中每个!负责,只有确定是安全的时候才应强制解包。

let danjia = "42"			// 如果 danjia 里面保存的值为 "单价",执行下面语句时会崩溃
let price = Int(danjia)!

5. Implicitly unwrapped optionals(隐式解包)test

即在声明变量时在类型后面加上!,这样今后使用时就不必解包了,当成非可选项处理。

let age: Int! = nil

由于这样操作将视同为已经解包,所以不需要if letguard let来处理。然而,如果真心没有值,是nil的话,那程序就崩溃了。

隐式解包一般会用在这样的场景,某个变量开始没有值,但一旦使用后就保证一直有值。这样因为保证有值,就可以少去使用if let 的麻烦。

6. Nil coalescing (nil 合并)test

假设有下面这样一个函数

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

如果调用的参数 id 为 15(反正不是 1 就行),将返回 nil。可以使用一个默认值来代替 nil。使用下面的方法

let user = username(for: 15) ?? "无名氏"

双问号的意思就是如果函数返回 nil,就取双问号后面的值。

7. Optional chaining(可选项链)test

Swift 提供了一种使用可选项的便捷方式:如果要访问 a.b.c. 并且其中 b 是可选项,那么可以使用可选链:a.b?.c

程序运行时,Swift 先检查 b 的值,如果是 nil,其他的就统统忽略,直接返回 nil;而如果有值则会按部就班地解包、执行。

let names = ["John", "Paul", "George", "Ringo"]		// Swift 自动推断为字符串数组

let beatle = names.first?.uppercased()				// 因为第一个元素有值,所以 beatle 的值为 “JOHN”

let names2: [String] = []		// 声明空数组时必须指明元素类型

let beatle2 = names2.first?.uppercased()	// 此时 first 不存在,为 nil,所以 beatle2 的值为 nil

8. Optional trytest

回忆一下第 5 天(Day 5)时曾经写过下面的程序。

我们将编写一个检查密码是否正确的函数,因此,如果用户尝试使用明显的密码,则会抛出错误:

enum PasswordError: Error {	// 自定义的错误,类型必须是 Error
    case obvious
}
func checkPassword(_ password: String) throws -> Bool {	// 遇到错误会抛出的函数,使用 throws 关键字定义
    if password == "password" {
        throw PasswordError.obvious		// 当 password 的值为 "password" 时,使用 throw 关键字抛出错误
    }
    return true
}
// 使用 `do...try...catch`捕捉错误。
do {
    try checkPassword("password")				// 尝试调用函数
    print("一切正常")						  	 // 没有遇到错误
} catch {
    print("对不起,出错了~~")					   // 遇到错误
}

这里可以有两种方法替换 try。第一种,使用 if let

if let result = try? checkPassword("password") {
    print("结果是 \(result)")
} else {
    print("完蛋,出错了。")
}

第二种,在确认有值的时候使用 try!,但是如果没有值,程序可不会报错而是直接崩溃。

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

9. Failable initializerstest

在写强制解包的时候,使用过下面的代码

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

这里将 str 转换成 Int 类型,但是因为 str 可以是任何东西,所以实际上返回的是一个可选的整数。这就是 failable initializer。

可以在自定义的结构体或者类中使用 init?() 来替代init(),如果有问题就返回nil。返回值就会成为期待类型的可选项,供解包用。

如,Person 结构体需要使用一个 9 个字母长的 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!")
    }
}

这里 Fish 和 Dog 都继承 Animal,所以我们可以创建下面的数组

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

那么 pets 的类型被推断为 Animal。如果遍历 pets 时遇到 Dog 调用 makeNoise 函数,就要使用关键字 as? 来返回一个可选项,这个可选项在类型转换失败的时候返回 nil。

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

11. Optionals summary(可选项小结)test

  • 可选项用于以清晰明确的方式表示值的缺失。
  • Swift不会允许在不解开可选参数的情况下使用if let或使用guard let
  • 可以使用感叹号强制打开可选的选项,但是如果强制打开nil的代码,则会崩溃。
  • 隐式解包没有常规可选项的安全检查。
  • 可以使用 nil 合并来解开可选值,如果其中没有任何内容,则提供默认值。关键字是 ??
  • 可选链可以用来操作可选控件,但是如果可选控件结果为空,则代码直接返回 nil,其他部分被忽略。
  • 您可以使用try?将throwing函数转换为可选的返回值,但是如果返回值为 nil 时程序会崩溃。
  • 如果在初始化输入错误时需要初始化失败时返回 nil,可以使用init?()替代init()
  • 可以使用类型转换将一种类型的对象转换为另一种类型
发布了77 篇原创文章 · 获赞 16 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/hh680821/article/details/105185306