数组值是否可选?

时间:2018-03-20 13:04:05

标签: swift

你能解释一下原因:

  • 当我使用array.first访问数组值时,它是可选的
  • 当我从索引值访问时它不是吗?

示例:

var players = ["Alice", "Bob", "Cindy", "Dan"]
let firstPlayer = players.first
print(firstPlayer) // Optional("Alice")
let firstIndex = players[0]
print(firstIndex) // Alice

5 个答案:

答案 0 :(得分:7)

(这个问题的简短答案非常棒,而且正是你所需要的。我只想更深入地了解为什么以及如何更普遍地与Swift集合交互以及底层类型。如果你只是想要“如何”我应该使用这些东西吗?“阅读接受的答案并忽略所有这些。”

数组遵循所有集合的规则。 Collection必须实现以下下标:

subscript(position: Self.Index) -> Self.Element { get }

因此,要成为集合,Array的下标必须接受其索引并无条件地返回Element。对于多种集合,不可能创建不存在的索引,但Array使用Int作为其索引,因此它必须处理您传递超出范围的索引的可能性。在这种情况下,不可能返回一个元素,它唯一的选择是根本无法返回。这通常采取崩溃程序的形式,因为它通常比挂起程序更有用,这是另一种选择。

(这隐藏了一点类型理论,即Swift中的每个函数在技术上都可以返回“崩溃”,但我们不会在类型系统中跟踪它。可以这样做来区分可以的函数崩溃和那些不能,但斯威夫特没有。)

当你使用不存在的键下标时,这自然会引发为什么Dictionary不会崩溃的问题。原因是词典的索引不是它的关键。它有一个很少使用的下标,它提供了对Collection的一致性(在顶层代码中很少使用,但在stdlib中很常用):

subscript(position: Dictionary<Key, Value>.Index) -> Dictionary.Element { get }

Array也可以这样做,具有独立于Int的Array.Index类型,并使Int下标返回Optional。在Swift 1.0中,我打开了一个雷达来要求。该团队认为,这将使得Array的常见用途变得非常困难,并且来到Swift的程序员习惯了超出范围是编程错误(崩溃)的想法。另一方面,字典对于使用不存在的键进行访问是很常见的,因此Key下标应该是Optional。使用Swift好几年让我确信他们是对的。

通常,除非从数组中获取索引(即使用index(where:)),否则不应下标数组。但是许多Cocoa模式使下标很自然(cellForRow(at:)是最着名的)。但是,在更纯粹的Swift代码中,使用任意Int进行下标通常会产生设计问题。

相反,您应该经常使用像firstfirst(where:)之类的Collection方法返回Optionals并且通常更安全,更清晰,并使用for-in循环而不是下标来迭代它们。

答案 1 :(得分:4)

如果您想使用下标并且不想发生崩溃,可以将此扩展名添加到您的代码中:

extension Collection {
    subscript (safe index: Index) -> Iterator.Element? {
        return indices.contains(index) ? self[index] : nil
   }
}

然后使用它:

let array = [0, 1, 2]
let second = array[safe:1] //Optional(1)
let fourth = array[safe:3] //nil instead of crash

答案 2 :(得分:3)

这是因为对于first,如果Array为空,则值为nil。这就是为什么它是可选的。如果它不为空,则返回第一个元素。

但是,使用下标(或索引值),程序将崩溃并出现错误

  

致命错误:索引超出范围

如果超出范围(或为空)并且不返回可选项。否则,它将返回所需的元素。

答案 3 :(得分:3)

first索引订阅的行为不同:

    安全地声明
  • first:如果数组为空,则返回nil,否则返回(可选)对象。
  • 由于遗留原因,
  • 索引订阅不安全:如果数组为空,则抛出超出范围的异常,否则返回(非可选)对象

答案 4 :(得分:0)

数组属性有默认行为。数组是Element的通用类型。当您尝试首先使用它时,它将作为可选项返回。

public var first: Element? { get }

这在Array类中可用。