可选项(optionals)
用来处理值可能缺失的情况。它允许将值设置为nil。一般也叫可选类型。
表示两种可能:
- 有值,你可以解析可选类型访问这个值
- 根本没有值。
?
在类型名称后面加个问号? 来定义一个可选项,表示该变量可能为空(可能包含值,也可能不包含值)
var sex: String? // sex 被自动设置为 nil
var name: String? = "Jack"
var age: Int? = 12
age = nil
var num: Int?
print(name,age, num)
// 打印结果 Optional("Jack") nil nil
var num: Int = nil。这样是不可能赋值成功的。因为Int类型中没有nil这个概念!
可选项是对其他类型(如
Int)的一层包装 。可以将它理解为一个盒子。 如果为nil,那么它是个空盒子; 如果不为nil,那么盒子里装的是:被包装类型的数据。
- nil在Swift 和 Objective-C 中并不一样。 在 Objective-C 中,nil 是一个指向不存在对象的指针。 在 Swift 中,nil 不是指针。它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。
解包
展开选项有两种方式
- if let
if let marioOpposite = opposites["Mario"] {
print("Mario's opposite is \(marioOpposite)")
}
这种if let语法在 Swift 中非常常见,它将创建条件 ( if) 与创建常量 ( let) 结合起来。它一起完成三件事:
- 它从字典中读取可选值。
- 如果可选项里面有一个字符串,它就会被解开——这意味着里面的字符串被放入marioOpposite常量中。
- 条件已成功 - 我们能够解开可选内容 - 因此条件的主体已运行。
仅当可选项内部有值时才会运行条件的主体。
因此,if let这是一种非常简洁的处理选项的方法,可以同时检查和提取值。
事实上,它非常简洁,甚至给我们提供了一些捷径。
if let number = number {
print(square(number: number))
}
我们实际上可以这样写:
if let number {
print(square(number: number))
}
它做了完全相同的事情——它创建了一个number仅在该条件体内展开的阴影副本,只是重复次数少了一点。
可选值最重要的一个特性是,Swift 不会让我们在不先解开它们的情况下使用它们
- guard let
func printSquare(of number: Int?) {
guard let number = number else {
print("Missing input")
return
}
print("\(number) x \(number) is \(number * number)")
}
- ??
let new = captains["Serenity"] ?? "N/A"
let savedData = first() ?? second() ?? ""
可用于提供默认值
- 可选链
let chosen = names.randomElement()?.uppercased() ?? "No one"
可选链接的神奇之处在于,如果可选值为空,它会默默地不执行任何操作 - 它只会发回与之前相同的可选值,但仍然为空。这意味着可选链的返回值始终是可选的,这就是为什么我们仍然需要 nil 合并来提供默认值。
- 强制解包
如果要取出被包装的数据,需使用感叹号! 进行强制解包。
强制解包前提是,确定可选项为非nil。
print(name)
// 打印结果 Optional("Jack")
print(name!)
// 打印结果 Jack
注意:如果对值为
nil的可选项强制解包,将会产生运行时错误。
- 隐式解包
如果确定一个可选类型总会有值。在这种情况下,每如果次都要判断和解析可选值,是非常低效的。这时,可以定义隐式解析可选类型。
在类型后面加个!,来定义一个隐式解包的可选项。
let num1: Int! = 10 // 表示该变量现在or未来 都有值
可选项绑定
用来 判断可选项是否包含值。如果包含,就自动解包,把值赋给一个临时量(let或var),并返回true,否则返回false。
- if语句
var age: Int?
if let number = age {
// 有值
// number是强制解包之后的 Int值
// number作用域仅限于这个大括号
print("字符串转换整数成功:\(number)")
} else {
print("空")
}
if let firstNum = Int("4"), let secondNum = Int("42"), firstNum < secondNum && secondNum < 100 {
print("\(firstNum ) < \(secondNum ) < 100")
}
- guard语句
guard 条件 else {
// do something....
}
当guard语句的条件为false时,就会执行{ }里面的代码;
为true时,就会跳过guard语句
注意:guard语句中绑定的常量let、变量var也能在else{ }外层作用域中使用
func login(_ info: [String : String]) {
guard let username = info["username"] else {
print("请输入用户名")
return
}
guard let password = info["password"] else {
print("请输入密码")
return
}
// if username ....
// if password ....
print("用户名:\(username)", "密码:\(password)", "登陆ing")
}
可选绑定可以用在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
空合并运算符 ??
如a ?? b (类似于三目运算符 a?a:b)
a 是可选项;b 可能是 可选项;b 跟 a 的类型必须相同
a ?? b表示:如果 a 不为nil,就返回 a;如果 a 为nil,就返回 b。如果 b 不是可选项,返回 a 时会自动解包。
与if 语句配合:
let a: Int? = nil // a是可选项,并且设为nil
let b: Int? = 2
if let c = a ?? b {
print(c) // 打印结果 2
}
可选链
先定义几个类
class Person {
var residence: Residence? // 可选类型 初始化为nil
}
class Residence {
var address: Address?
var rooms = [Room]() // 数组
var numberOfRooms: Int {
return rooms.count
}
// 通过下标,返回数组对应的元素
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
}
class Room {
let name: String
init(name: String) {
self.name = name
}
}
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if buildingName != nil {
return buildingName
} else if let buildingNumber = buildingNumber, let street = street {
return "\(buildingNumber) \(street)"
} else {
return nil
}
}
}
通过可选 链式调用
- 访问属性
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("有 roomCount 个房间")
} else {
print("不能拿到rooms的个数")
}
// 因为 john.residence 为 nil,所以这个可选链式调用 失败
- 设置属性
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
// 通过 john.residence 来设定 address 属性也会失败
// 因为 john.residence 当前为 nil。
可选链式调用失败时,等号右侧的代码不会被执行。
- 调用方法 通过可选链式调用得到的返回值都是可选的
if john.residence?.printNumberOfRooms() != nil {
} else {
print("不可能打印the number of rooms.")
}
- 访问下标 通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功。
if let firstRoomName = john.residence?[0].name {
} else {
print("查不到值")
}
通过下标,用可选链式调用来赋值
john.residence?[0] = Room(name: "Bathroom")
连接多层可选链式调用
通过可选链式调用访问一个 Int 值,将会返回 Int?,无论使用了多少层可选链式调用。 类似的,通过可选链式调用访问 Int? 值,依旧会返回 Int? 值,并不会返回 Int??。
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
}
在方法的可选返回值上进行可选链式调用
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
如果要在该方法的返回值上进行可选链式调用,在方法的圆括号后面加上问号即可:
if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
}
}
在上面的例子中,在方法的圆括号后面加上问号是因为你要在 buildingIdentifier() 方法的可选返回值上进行可选链式调用,而不是 buildingIdentifier() 方法本身。