iOS一点通

可选类型? 对optional变量拆包有多少种方法

2020-03-19  本文已影响0人  红色海_

可选类型的介绍

◦ 概念:
▪ 在OC开发中,如果一个变量暂时不使用, 可以赋值为0(基本属性类型)或者 赋值为空(对象类型)
▪ 在swift开发中, nil也是一个特殊的类型. 因为和真实的类型不匹配是不能赋值的(swift是强类型语言)
▪ 但是开发中赋值nil, 在所难免.因此推出了可选类型

在Swift中Optional(可选类型)是一个含有两种情况的枚举,None 和 Some(T),

用来表示 (可能有) 或 (可能没有值)。

任何类型都可以明确声明为(或者隐式转换)可选类型。
当声明一个可选类型的时候,要确保用括号给 ? 操作符一个合适的范围。

◦ 可选类型的取值:
▪ 空值
▪ 有值

注意:

可选变量可以使用nil赋值表示它没有值;
但nil不能用于非可选的变量和常量

定义可选类型
◦ 定义一个可选类型有两种写法
▪ 最基本的写法
▪ 语法糖(常用)

// 错误写法
// let string : String = nil
// 正确写法:
//
// 注意:name的类型是一个可选类型,但是该可选类型中可以存放字符串.

最基本的写法


// 写法一:定义可选类型
let name : Optional<String> = nil

语法糖(常用)

// 写法二:定义可选类型,
let name : String? = nil
注意:在类型和 ?之间没有空格
可选类型的使用

// 演练一:给可选类型赋值
// 定义可选类型
var string : Optional<String> = nil

// 给可选类型赋值
// 错误写法:因此该可选类型中只能存放字符串
string = 123
// 正确写法:
string = "Hello world"

// 打印结果
print(string)
// 结果:Optional("Hello world")\n
// 因为打印出来的是可选类型,所有会带Optional


// 演练二:取出可选类型的值
// 取出可选类型的真实值(解包)
print(string!)
// 结果:Hello world\n

// 注意:如果可选类型为nil,强制取出其中的值(解包),会出错
string = nil
print(string!) // 报错

// 正确写法:
if string != nil {
    print(string!)
}

// 简单写法:为了让在if语句中可以方便使用string
// 可选绑定
if let str = string {
    print(str)
}

真实应用场景

    ◦   目的:让代码更加严谨
// 通过该方法创建的URL,可能有值,也可能没有值.
// 错误写法:如果返回值是nil时,就不能接收了
// 如果字符串中有中文,则返回值为nil,因此该方法的返回值就是一个可选类型,而使用一个NSURL类型接收是错误的
let url : NSURL = NSURL(string: "www.520it.com")

// 正确写法:使用可选类型来接收
let url : NSURL? = NSURL(string: "www.520it.com")
// 该方式利用类型推导
let url = NSURL(string: "www.520it.com")

// 通过url来创建request对象:在使用可选类型前要先进行判断是否有值
// 该语法成为可选绑定(如果url有值就解包赋值给tempURL,并且执行{})
if let tempUrl = url {
    let request = NSURLRequest(URL: tempUrl)
}



1、强制拆包 !操作符——不安全

//注意:如果可选类型为nil,强制取出其中的值(解包),会出错
var string : Optional<String> = nil
print(string!)

// 正确写法:
if string != nil {
print(string!)
}

2、隐式拆包变量声明——大多数情况下不安全

var str: String! = "hello"
str.append(" world!")

隐式解析可选的值会在使用时自动解析,所以没必要使用操作符!来解析它。也就是说,如果你使用值为nil的隐式解析可选,就会导致运行错误。

什么时候必须要用?
1、对象属性在初始化的时候不能nil,否则不能被初始化。典型的例子是Interface Builder outlet类型的属性,它总是在它的拥有者初始化之后再初始化。在这种特定的情况下,假设它在Interface Builder中被正确的配置——outlet被使用之前,保证它不为nil。

2、解决强引用的循环问题——当两个实例对象相互引用,并且对引用的实例对象的值要求不能为nil时候。在这种情况下,引用的一方可以标记为unowned,另一方使用隐式拆包。

建议:除非必要,不要对option类型使用隐式拆包。使用不当会增加运行时崩溃的可能性。在某些情况下,崩溃可能是有意的行为,但有更好的方法来达到相同的结果,例如,通过使用fatalError( )函数。

3、可选绑定——安全

if let str = string {
print(str)
}

4、自判断链(optional chaining)——安全

也叫 可选值链

//1. 概述
当某个可选值可能为空时,如果要访问它的属性、方法、下标脚本,需要使用Optional chaining 对它进行解包,然后进行访问。

如果这个可选值包含一个值,那么访问成功。
如果这个可选值为 nil ,那么访问返回nil。

多个访问可以组合在一起,成为一个访问链条,任何一个链条为nil,整个链条返回nil。

//2. 使用Optional Chaining代替强制拆包
如果某个可选值是 non-nil 的,你想访问它的属性、方法和下标脚本时,
通过在可选值后面加上 (?) 指定为 Optional Chaining。这个与使用 (!) 对可选值进行强制拆包类似。
主要不同点在于当可选值为 nil 时,
如果使用 Optional Chaining ,那么返回 nil;
如果使用强制拆包,那么将触发运行时错误。

为了反映 Optional Chaining 可以调用空(nil),
不论你调用的属性、方法、下标脚本等返回的值
是不是可选值类型,它的返回结果都是可选值类型。
你可利用这个返回值来检测你的Optional Chaining是否调用成功,
有返回值即成功,返回nil则失败。

调用 Optional Chaining 的返回结果与原本的返回结果具有相同的类型,
但是原本的返回结果被包装成了一个可选值类型,
当 Optional Chaining 调用成功时,
一个应该返回Int的属性将会返回Int?。

下面几段代码将解释自判断链接和强制拆包的不同。

    class Person {
      var residence: Residence?
    }
    class Residence {
      var numberOfRooms = 1
    }

如果你创建一个新的Person实例,它的residence属性由于是被定义为自判断型的,此属性将默认初始化为空:

let john = Person()

如果你想使用声明符!强制拆包获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供拆包的residence值。

    let roomCount = john.residence!.numberOfRooms
    // this triggers a runtime error

当john.residence不是nil时,会运行通过,且会将roomCount 设置为一个int类型的合理值。然而,如上所述,当residence为空时,这个代码将会导致运行时错误。

Optional Chaining 提供了一种另一种获得numberOfRooms的方法。利用Optional Chaining,使用 ? 来代替原来 !的位置:

 if let roomCount = john.residence?.numberOfRooms {
   println("John's residence has \(roomCount) room(s).")
} else {
   println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."

这告诉Swift来链接可选值类型的属性 residence?,如果residence存在则取回numberOfRooms的值。

因为访问 numberOfRooms 存在失败的可能性,所以Optional chaining 返回一个 Int? 类型的值。在上面的例子中,当residence为空时,Int? 也是nil。

虽然 numberOfRooms 不是Int? 类型,由于它在 Optional Chainning 中被访问,它将返回 Int? 类型,而不是 Int 类型。

5、nil coalescing 运算符(空值合并运算符)——安全

print(optional ?? 0)
// swift3.0以后 不支持

6、Swift 2.0 的新特性 guard 语句——安全

func testStr(aString:String?) {
    guard let str = aString else {
        print("没值, 退出!")
        return
    }
    // str is available to use
    print("Satisfied!")
}
guard 和if 的可选绑定有点类似,但是guard更好。

guard作用: 
* 1. 对你所期望的条件做检查。
   如果条件不符合,guard的else语句就运行,
   从而退出这个函数。所以要在else代码块中退出操作 
* 2. 如果通过了条件判断,
   可选类型的变量在guard语句被调用的范围内
   会被自动的拆包。(安全解包)

7、Swift 2.0 的新特性optional pattern(可选模式)

pattern matching??究竟叫什么?if case ?

var t : Any? = 10
//判断t是不是nil,如果有值则绑定到x
if case let x? = t {
   print(x)
}
上一篇下一篇

猜你喜欢

热点阅读