可选类型
可选类型
处理丢失数据
使用Int等类型来保整数。但是,如果想为用户存储age属性,如果你不知道某人的年龄,你会怎么做? 或许可以使用1000或-1等特殊数字来表示“未知”,这两个数字都是不可能的年龄,但真的会记得它使用的所有地方的这个数字吗? Swift的解决方案是可选类型,可以制作任何类型的可选类型。可选整数可能有一个像0或40这样的数字,但可能根本没有值——nil。 要使类型可选,请在它后面添加一个问号:
var age: Int? = nil
可选类型解包
可选字符串可能包含像“你好”这样的字符串,或者它们可能是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.")
}
guard let
guard let 解包可选类型,如果里面发现nil,它期望退出使用的函数、循环或条件。 使用guard let允许在功能开始时处理问题,然后立即return。
func greet(_ name: String?) {
guard let unwrapped = name else {
print("You didn't provide a name!")
return
}
print("Hello, \(unwrapped)!")
}
强制解包
可选类型表示可能存在或不存在的数据,但有时可以确保值不是nil。 在这些情况下,Swift允许您强制解包可选:将其从可选类型转换为非可选类型。
let str = "5"
let num = Int(str)
这使得num成为可选的Int,可以通过强制解包结果!:
let num = Int(str)!
只有当确定是安全的时才应该强制解包——这是它通常被称为崩溃操作员的原因。
隐式拆包可选类型
与常规可选件一样,隐式未包装的可选件可能包含一个值,或者它们可能为nil。无需拆开包装即可使用,当做不是可选类型来使用。如果是nil代码就会崩溃。 通过在类型名称后添加感叹号来创建隐式拆包可选类型:
let age: Int! = nil
nil 合并解包
这是一个接受整数作为其唯一参数并返回可选字符串的函数:
func username(for id: Int) -> String? {
if id == 1 {
return "Taylor Swift"
} else {
return nil
}
}
如果用id15调用将返回nil,因为不被识别,但通过nil合并可以提供“匿名”的默认值:
let user = username(for: 15) ?? "Anonymous"
可选类型链
当使用可选时,Swift提供了一个快捷方式:如果访问像a.b.c和b是可选的,可以在它后面写一个问号以启用可选链接:a.b?.c。 代码运行时Swift将检查b是否有值,如果为nil则忽略行的其余部分——Swift将立即返回nil。但如果有值,将被解包并继续执行。
let names = ["John", "Paul", "George", "Ringo"]
使用数组first属性,如果数组有first项则返回第一个名称,如果数组为空则返回nil。然后可以在结果上调用uppercased()以使其成为大写字符串:
let beatle = names.first?.uppercased()
抛出函数的try
运行一个抛出函数,使用do、try和catch优雅地处理错误:
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.")
}
有两种选择可以try,既然你理解了可选的选项并强制解包,这两种选择都会更有意义。
- try? 抛出函数更改为返回可选函数的函数。如果函数抛出错误,将收到nil作为结果,否则将获得作为可选包装的返回值:
if let result = try? checkPassword("password") {
print("Result was \(result)")
} else {
print("D'oh.")
}
- try! 当您确定该功能不会失败时可以使用它。如果函数确实抛出错误代码将崩溃。
try! checkPassword("sekrit")
print("OK!")
可失败的初始化
一个可能成功或可能失败的初始化器。可以使用init?()在结构和类中编写这些而不是init()。如果出现问题,则返回nil。然后,返回值将是类型的可选值,可以随心所欲地解包。
struct Person {
var id: String
init?(id: String) {
if id.count == 9 {
self.id = id
} else {
return nil
}
}
}
类型选择 (Typecasting)
这里有三类:
class Animal { }
class Fish: Animal { }
class Dog: Animal {
func makeNoise() {
print("Woof!")
}
}
创建几条鱼和几条狗,并将它们放入一个数组: 这里有三类:
let pets = [Fish(), Dog(), Fish(), Dog()]
在pets数组上循环并执行makeNoise函数,需要执行类型转换:Swift将检查每个对象是否是Dog对象,如果是可以调用makeNoise() 这使用了一个名为as?的新关键字,该关键字返回一个可选类型:如果类型转换失败,则为nil:
for pet in pets {
if let dog = pet as? Dog {
dog.makeNoise()
}
}