你能解释一下原因:
示例:
var players = ["Alice", "Bob", "Cindy", "Dan"]
let firstPlayer = players.first
print(firstPlayer) // Optional("Alice")
let firstIndex = players[0]
print(firstIndex) // Alice
答案 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进行下标通常会产生设计问题。
相反,您应该经常使用像first
和first(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类中可用。