Swift可选链

时间:2020-02-23 14:44:02  来源:igfitidea点击:

Swift Optional Chaining是一个非常有用的功能。
可选是Swift中包装原始类型的一种类型,通常用于防止空值。
有关Optionals的更多详细信息,请在继续之前参考本教程。

Swift可选链

可选链接是对可选值链接调用的过程,以便它可以优雅,简洁地处理展开操作,而不会产生运行时错误。
顾名思义,它将多个查询链接在一起。

但是使用if letguard let和隐式解包也是可以实现的。

那么,对可选链接有何需求?

让我们先来看一下Playground中的示例,而无需带来"可选链接"。

没有可选链接

假设我们有以下程序:

class University {
  var city: City?
  var universityName: String = "San Francecon Institute of Technology"
}

class City{
  
  var cityName: String?
  var college: College?
  
}

class College{
  
  var discplines = [Discpline?]()
  var numberOfStreams : Int
  {
      return discplines.count
  }
  
}

class Discpline{
  var discplineName: String?
  var student : Student?
}

class Student{
  var name: String?
  var discpline : Discpline
  init(currentDiscpline: Discpline) {
      self.discpline = currentDiscpline
  }
  
  func printDetails() ->String  {
      return "Student Details:\n Name:\(name ?? "Na") \n Discpline: \(discpline.discplineName ?? "NA")"
  }
}

每个类都包含以下类的实例。
所有实例均定义为可选。
"大学"类拥有一个" Discpline"类对象的数组。

让我们实例化每个类。

var university = University()
var myCity = City()
myCity.cityName = "Bangalore"
var myCollege = College()
var csDiscpline = Discpline()
csDiscpline.discplineName = "Computer Science"
var meDiscpline = Discpline()
meDiscpline.discplineName = "Mechanical Engineering"
myCollege.discplines = [csDiscpline,meDiscpline]

var myStudent = Student(currentDiscpline: csDiscpline)
myStudent.name = "Anupam"

使用隐式展开让我们使用隐式展开(!)从类中获取和检索属性和函数。

university.city = myCity
university.city!.college = myCollege
university.city!.college!.discplines = [csDiscpline,meDiscpline]
university.city!.college!.discplines[0]!.student = myStudent
var finalString = university.city!.college!.discplines[0]!.student!.printDetails()

看起来还好但是我们从Swift的Optionals教程中知道,隐式展开可能会导致运行时崩溃,因为它会在不检查是否为零的情况下解开可选的内容。

在上面的代码中,如果将myStudent设置为nil,则将导致CRASH。

university.city!.college!.discplines[0]!.student = nil
var finalString = university.city!.college!.discplines[0]!.student!.printDetails() //crash since student is nil

让我们尝试使用if let/if var

if var uCity = university.city
{
  uCity = myCity
  if var uCollege = uCity.college
  {
      uCollege = myCollege
      uCollege.discplines = [csDiscpline,meDiscpline]
      if var uDiscpline = uCollege.discplines[0]
      {
          if var uStudent = uDiscpline.student
          {   uStudent = myStudent
              print(uStudent.printDetails())
          }
      }
  }
}

嵌套太多!这也仅仅是为了设置值并以非常基本的代码打印结果,而每个类中几乎没有几个属性。

这是低效的。

使用guard var让我们使用guard var做同样的事情。

func usingGuard(){
guard var uCity = university.city else{
  print("City is nil")
  return
}
uCity = myCity

guard var uCollege = uCity.college else{
  print("College is nil")
  return
}
uCollege = myCollege
uCollege.discplines = [csDiscpline,meDiscpline]

guard var uDiscpline = uCollege.discplines[0] else{
  print("Discpline is nil")
  return
}

guard var uStudent = uDiscpline.student else{
  print("Student is nil")
  return
}
uStudent = myStudent
print(uStudent.printDetails())
}

usingGuard()
//prints
//Student Details:
//Name:Anupam 
//Discpline: Computer Science

这比var更好,但仍然有太多条件检查。
隐式包装简洁明了,但很危险,因为它在包装前并未检查可选值。

这是"可选链接"为我们提供帮助的地方。

这是强制展开的另一种更好的形式。

在可选链接中,我们只需要将!.替换为?.

可选链接如何工作

可选链接是在可选值上完成的。
它返回包装为Optional的所需值。
如果optional为nil,则返回Optional(nil)
可选链接始终为您提供可选选项,从而消除了运行时崩溃的机会。

因此有两件事:

  • 如果通过"可选链接"检索的类型不是可选的,则在完成可选链接后,该类型将变为可选。

  • 如果类型已经是可选的,那么它将仅保持可选。
    它不会像Optional(Optional(Optional(String)))那样嵌套。

使用可选链接实施

以下代码实现了可选的链接。

university.city = myCity
university.city?.college = myCollege
university.city?.college?.discplines = [csDiscpline,meDiscpline]
university.city?.college?.discplines[0]!.student = myStudent
var finalString = university.city?.college?.discplines[0]?.student?.printDetails()
print(finalString)

现在定义了printDetails()返回一个String。

可以?没有。

它返回Optional(String),因为Optional Chaining用可选的包装了返回的值。

通过可选链接访问下标调用

将Discpline类更改为:

class College{
  
  var discplines = [Discpline?]()
  var numberOfStreams : Int
  {
      return discplines.count
  }
  subscript(i: Int) -> Discpline? {
      get {
          return discplines[i]
      }
      set {
          discplines[i] = newValue
      }
  }
  
}

多亏了下标,我们可以摆脱纪律对象的调用。

if let discpline0 = university.city?.college?[0]?.discplineName
{
  print(discpline0)
}

注意:通过可选链接访问可选值上的下标时,您将问号放在下标的括号之前,而不是之后。