可选类型
1、概念
可选类型是伴随着Swift诞生的一个类型,在原有的OC中是不存在的,究其缘由,是因为Swift是类型安全的语言,而OC则是弱类型语言。OC中String字符串既可以是nil,可以是字符串。而在Swift中,这俩种状态是不能同时存在的。并且,OC和Swift对于nil的解释也是不太一样的。nil的解释:
1).Objective-C中的nil:表示缺少一个合法的对象,是指向不存在对象的指针,对结构体、枚举等类型不起作用(会返回NSNotFound)2).Swift中的nil:表示任意类型的值缺失,是一个确定的值,要么是该类型的一个值要么什么都没有(即为nil)
在Swift中Optional(可选类型)是一个含有两种情况的枚举,None和Some(T),用来表示可能有或可能没有的值。任何类型都可以明确声明或者隐式转换为可选类型。简单来说,一个变量没有默认值这一说法,要么有值,要么没有值nil。
1.1 声明
Swift中的可选类型是一种单独的数据类型,相应的也有非可选类型。其定义如下:
var optionalStr: String? = "swift语言可选类型"//声明可选类型字符串,并赋初值 var opStu:Student? //声明可选opStu对象,值为nil var c:Int!//声明为可自动解包的可选类型,值为nil,即没有值这里需要注意一下,在类型和?之间是没有空格的。对应的非可选类型是什么样的呢?刚刚说了,当可选类型的变量没有赋值的时候,它们就处于没有值的状态。那非可选类型,就是不能处于这种没有值状态下的类型,比如下面的定义:
var a :Int//这是错误的定义方式,在Swift中是无法通过的在OC或C/C++语言中,是允许声明一个变量的时候不进行赋值的情况,即:
int a = 0;//定义一个int型变量,并赋值为0 int a;//定义了一个int型变量,虽然没有显示的指定其值,但是默认的设置了一个0在开发中,如果要对有值和没值的情况进行区分,毕竟没有赋值与默认设置一个初始值还是两个概念的。在OC中是没有专门的语法去支持的,实现起来需要额外的变量来做辅助,比较麻烦。但是,Swift中就显得小菜一碟了,出于这种需求,可选类型就应运而生了,就像上面所说,专门去定义了变量有值和没值的情况。
1.2 强制解析(强制拆包)
可选类型,是一个单独的数据类型。赋值没有什么特别的,和其他类型没有什么区别,但是取值就有所不同了。首先看一下下面的打印结果:
//定义一个可选类型的变量 var e :Int?//处于没有值的状态 e = 3//当前的a,是有值的状态 print(e) e = nil//当前的e,又变成了一个没有值的状态在打印这个变量的时候,可以看到实际的值被一个Optional包了起来:
然后,如果我们打印print(e+3)的话,就会报错如下:
这说明了,可选类型是不能直接使用的。要获取其值,那么就是可选类型的强制解包和自动解包了:
当确定自定义的可选类型一定有值的时候,就可以使用操作符!进行强制解析,拿到数据。!表示“我知道一定有值,请使用它”。当我们判断错误,在可选值为nil的时候使用!进行强制拆包,会出现运行时错误。因此,在使用!进行强制解包的时候,一般是搭配if判断去使用,当判断可选类型的值不为nil的时候,再去拆包。
var myStr:String? = nil
myStr="强制解析,一定有值"
if myStr != nil {
print(myStr!)//使用!进行强制解析
}else{
print("字符串为nil")
}
//执行结果
强制解析,一定有值
1.3 自动解析
在最初的声明的时候使用?修饰Optional可选类型的,当我们希望可以自动解析的话,则用!来代替?去修饰声明。这样,在变量使用的时候就不需要使用!去强制拆包,而是会自动解析。
var myStr:String! //使用!修饰
myStr="自动解析"
if myStr != nil {
print(myStr)
}else{
print("字符串为nil")
}
//执行结果
自动解析
1.4 可选绑定
使用可选绑定,就不需要频繁的去判断是否其值等于nil。但是,使用可选绑定Optional binding来判断可选类型是否包含值,如果包含就要把值赋给一个临时变量或者常量。可选绑定可以用于if和while语句中,来判断可选类型的值,然后将其值赋给一个常量或变量。
var myStr:String?
myStr="可选绑定"
if let tempStr = myStr {
print(tempStr)
}else{
print("字符串为nil")
}
//运行结果
可选绑定
1.5 guard let
在可选类型判断中海油guard let的用法,guard是守卫的意思。guard let和if let的意思刚好相反,guard let守护一定有值,如果没有就直接返回。
let name: String? = "张三"
let age: Int? = 10
guard let nameNew = name,
let ageNew = age
else {
print("姓名 或 年龄 为nil")
return
}
print("姓名 或 年龄 为\(name),\(age)")//运行到这里是一定有值的
1.6 双问号取值
空合运算符??是对三目运算符的简化,针对可选类型进行空判断,若非空怎么返回??运算符前的变量的值,为空则返回??后的默认值。它的使用有两个条件:
- a 必须是可选类型
- b 的类型必须与 a 一致
//如果e有值,则获取e中的值 //没有值,则赋值为5 let a = b??5 //所以a的值要么是b的值,要么就是5 print(a)//所以运行到这里,a也是一定有值的
1.7 as!与as?的类型转换
在写Swift代码的时候,我们时常会被as、as!、as?三种类型转换操作符混淆,总是会拿不准要去使用哪个,有时候会根据编辑器的提示去Fix code,但是这样并不能实际解决问题。
1.7.1 as
- 向上转型upcasts,从派生类转换为基类
// 定义人员基类 class Person {
var name : String init(_ name: String){
self.name = name
}
} // 定义学生类 class Student : Person {
} // 定义教师类 class Teacher : Person {
} // 处理人员对象的函数(或工厂模式处理操作等)
func showPersonName(_ people : Person){
let name = people.name print("这个人的名字是: \(name)")
}
// 定义一个学生对象 tom
var tom = Student("Tom");
// 定义一个教师对象 kevin
var kevin = Teacher("Kevin Jakson"); // 先把学生对象向上转型为一般的人员对象
let person1 = tom as Person
let person2 = kevin as Person // 再调用通用的处理人员对象的showPersonName函数
showPersonName(person1)
showPersonName(person2)</pre>
//运行结果
这个人的名字是:Tom
这个人的名字是:Kevin Jakson
- 消除二义性,数值类型转换
let age = 28 as Int let money = 20 as CGFloat let cost = (50 / 2) as Double
- 在switch语句中进行模式匹配
switch person1 {
case let person1 as Student:
print("是Student类型,打印学生成绩单...")
case let person1 as Teacher:
print("是Teacher类型,打印老师工资单...")
default: break
}
//运行结果
是Student类型,打印学生成绩单
1.7.2 as!
向下转型(Downcasting)时使用。由于是强制类型转换,如果转换失败会报 runtime 运行错误。就是说强制从父类转换成子类。
let person : Person = Teacher("Jimmy Lee")
let jimmy = person as! Teacher
1.7.3 as?
as?和as!操作符的转换规则是完全一样的。但as?在转换不成功的时候会返回一个nil对象,成功则返回可选类型的值。由于as?在转换失败的情况下也不会出现运行错误,所以对于那些能够保证100%会成功的转换使用as!,否则的话就使用as?
let person : Person = Teacher("Jimmy Lee") if let someone = person as? Teacher{ print("这个人是教师, 名字是 \(someone.name)") } else { print("这个人不是教师, 'tom'的值是 nil") }
2、使用场景
2.1 函数或方法的返回类型为可选类型
func returnOptionValue(value: Bool) -> String? { // 返回类型为可选String类型
if value {
return "返回类型是可选类型值"
} else {
return nil //返回nil
}
}
let optionValue = returnOptionValue(value: true) // 要用可选绑定判断再使用,因为returnOptionValue为String?可选类型
if let value = optionValue {
print(value)
} else {
print("none value")
}
//执行结果
返回类型是可选类型值
返回类型为闭包可选:
func returnOptionalFunc(value: Bool) -> (() -> (Void))? { // 返回类型为可选类型的闭包
if value {
return { () in
print("返回类型是可选类型闭包")
}
} else {
return nil
}
}
let possibleFunc = returnOptionalFunc(value: true) // 要用可选绑定判断再使用,因为possibleFunc 为可选类型的闭包,类型为() -> (Void)
if let aFunc = possibleFunc {
print(aFunc()) // 注意增加()调用闭包,因为没有参数则是空括号
} else {
print("none func")
}
//执行结果 返回类型是可选类型闭包
2.2 可选类型在类或结构体中的运用
在类中的使用:
class PossibleClass {
var someValue: Int
var possibleValue: String? // 可选存储属性,默认值为nil
init(someValue: Int) { // 构造方法中可以不对possibleValue属性初始化
self.someValue = someValue //但是someValue是非可选类型变量,所以在构造方法中,一定要对其进行初始化
}
}
let someClass = PossibleClass(someValue: 4)
在结构体中的应用:
struct PossibleStruct {
var someValue: Int
var possibleValue: String? // 可选存储属性,默认值为nil
// 结构体中可以自定义一个init构造器对属性初始化,也可以不自定义
}
let someStruct = PossibleStruct(someValue: 4, possibleValue: nil)
2.3 可选类型在构造器中使用
class PossibleStructInit {
let someValue: String
init?(someValue: String) { // 可失败构造器
if someValue.isEmpty { return nil } // 如果实例化为空串,则返回nil(即实例化失败)
self.someValue = someValue
}
}
let oneStruct = PossibleStructInit(someValue: "构造器中使用") // abc不是空串,oneStruct为PossibleStructInit?可选类型
if let one = oneStruct { // 使用if let可选绑定判断
print(one.someValue)
} else {
print("none value")
}
//输入结果为:构造器中使用
let twoStruct = PossibleStructInit(someValue: "") // 传参为空串
if let two = twoStruct {
print(two.someValue)
} else {
print("none value")
}
//输入结果为:none value
2.4 可选类型在错误处理中使用(try!与try?)
try?会将错误转换为可选值,当调用try?+函数或方法语句时候,如果函数或方法抛出错误,程序不会发崩溃,而返回一个nil,如果没有抛出错误则返回可选值。 使用try!可以打破错误传播链条。错误抛出后传播给它的调用者,这样就形成了一个传播链条,但有的时候确实不想让错误传播下去,可以使用try!语句。
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
// 上述语句中在执行loadImage方法时如果执行失败,使用try!来禁用错误传递,会有运行错误导致App崩溃
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction() // x可能正常返回一个Int类型的值也有可能抛出一个错误异常,使用时对x用if let可选绑定判断
查看7道真题和解析