Swift中 Nil Coalescing 运算符的使用技巧

移动开发 iOS
在Swift官方《The Swift Programming Language》文档 Beta 5 版本中的 Basic Operators 一节中添加了Nil Coalescing Operator小结,介绍了一个新的运算符Nil Coalescing,符号是 ?? ,它的表现形成如下:

在Swift官方《The Swift Programming Language》文档 Beta 5 版本中的 Basic Operators 一节中添加了Nil Coalescing Operator小结,介绍了一个新的运算符Nil Coalescing,符号是 ?? ,它的表现形成如下:

  1. let c = a ?? b 

这个运算符有两个条件:

a. 必须是Optional类型的。

b. 的类型必须要和a解包后的值类型一致。

符合这两个条件后,我们来解释一下上述这行代码,意思就 是c 的值是 a 或 b 中一个的值,但有前提条件,就是当 a 解包后值不为 nil 时,那么就将 a 解包后的值赋值给 c,如果 a 解包后值为 nil,那么就将 b 的值赋值给c。

我们还可以用三目运算来更形象的解释这个运算符:

  1. let c = a != nil ? a! : b 

从上面的代码我们很容易理解,当a的值不等于nil时,将a解包后的值赋值给c,否则将b的值赋值给c。

我们来看看官方给的代码示例:

  1. let defaultColorName = "red" 
  2.     var userDefinedColorName: String?   // 默认值为nil 
  3.     var colorNameToUse = userDefinedColorName ?? defaultColorName 
  4.     // 因为userDefinedColorName的值是nil,所以colorNameToUse的值为"red"
  1. userDefinedColorName = "green" 
  2.     colorNameToUse = userDefinedColorName ?? defaultColorName 
  3.     // 因为userDefinedColorName的值不为nil,所以colorNameToUse的值为"green" 

大家看到这应该对 Nil Coalescing 这个运算符有比较清晰的理解了,但在实际运用中,我们还需要注意以下几点。

编译器中的类型匹配原则

原谅我在末尾加的 ; ,编码习惯而已 = =||

我们先来看一段代码:

  1. let a: Int? = nil; 
  2.     let b: Int? = 7; 
  3.  
  4.     let c = a != nil ? a! : b; // 因为a的默认值为nil,所以c的值为{Some 7} 
  5.     let d = a ?? b; // 这里d的值为nil,这是怎么回事?

首先我们需要注意的是,在官方文档中有这么一句话:“The expression b must match the type that is stored inside a”。但是上面的代码示例中,我们的 b 是 Int? 类型,那么此时我们的编译器会怎么处理呢?我们通过自己实现一个 Nil Coalescing 运算符来说明,代码片段如下:

  1. infix operator ??? { 
  2.         associativity right; 
  3.         precedence 110; 
  4.     } 
  5.  
  6.     func ???<T>(a: T?, b: @autoclosure () -> T) -> T { 
  7.         return a != nil ? a! : b(); 
  8.     } 
  9.  
  10.     let d = a ??? b; // 这里d的值仍然是nil 

从上面的代码片段中我们可以看出,a的类型是根据传入的b的类型决定的,所以当我们传入的 b 是 Int? 类型时,编译器其实将 a 的类型自动转换为 Int?? 类型了,也就是 Optional(a) ,那么我们就能解释 let d = a ?? b; 这行代码。因为 a == nil 但是 Optional(a) != nil,所以 d = Optional(a)! ,d 的值为 nil 。

Nil Coalescing 运算符返回值的类型

我们先看示例代码片段:

  1. let a: Int? = nil; 
  2.     let b: Int? = 5; 
  3.     let c: Int? = 6; 
  4.  
  5.     // 因为a的默认值为nil,所以将b的值赋值给x,x的值为{Some 5},类型为Int? 
  6.     let x = a != nil ? a! : b; 
  7.     // 因为c的值不为nil,所以将c解包后的值赋值给y,但是y的值却是{Some 6},而不是6,这是怎么回事? 
  8.     let y = c != nil ? c! : b; 

根据上面我们提到的编译器中类型判断的原则就可以理解了,因为b的类型是Int?,所以在编译时 c 的类型已经成了 Int?? ,所以为 Int?? 解包的类型就是 Int? 了。

如果我们声明变量 y 的类型,编译器就要提出抗议了:

  1. // 如何a为Int?类型,那么编译器就不会通过,因为会导致赋值运算符两边类型不等 
  2.     let x: Int = a != nil ? a! : b; 
  3.   
  4.     // 这种写法和上面一样,都不会编译通过 
  5.     let y: Int = a ?? b;
责任编辑:chenqingxiang 来源: cocoachina
相关推荐

2022-09-07 00:04:37

JavaScript运算符技巧

2021-07-12 15:35:56

JavaScript代码运算符

2010-01-19 15:30:44

VB.NET比较运算符

2020-06-05 14:11:21

Swift运算符代码

2009-08-11 15:51:08

C#运算符算术运算符

2021-05-07 06:27:29

JavaScript运算符开发

2023-03-01 15:18:01

JavaScripttypeof运算符

2023-04-10 08:58:13

C#关系运算符

2009-08-12 15:20:18

C#赋值运算符复合赋值运算符

2009-08-12 15:02:49

C#赋值运算符简单赋值运算符

2009-11-18 09:37:31

PHP逻辑运算符

2009-11-18 09:02:55

PHP运算符

2010-03-16 11:20:38

Python格式化

2009-11-26 11:12:07

PHP运算符

2023-09-07 23:30:47

运算符C++

2018-12-14 10:00:16

BashLinux命令

2024-02-26 15:17:20

2021-12-15 10:25:57

C++运算符重载

2009-06-21 13:48:05

ShellLinux运算符

2009-08-12 09:30:10

C#??运算符
点赞
收藏

51CTO技术栈公众号