可选类型

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可选绑定判断



全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务