你是对的,选项可能会很痛苦,这就是为什么你不应该过度使用它们。但它们不仅仅是使用框架时必须处理的事情。它们是一个非常常见的问题的解决方案:如何处理返回可能正常或可能不正常结果的调用。
例如,采取Array.first
成员。这是一个方便的实用程序,可以为您提供数组的第一个元素。为什么能够调用很有用a.first
,当你可以打电话a[0]
?因为在运行时数组可能为空,在这种情况下a[0]
会爆炸。当然你可以检查a.count
事先——但话又说回来
A。你可能会忘记,
and
b.这会导致代码非常难看。
Array.first
通过返回一个可选值来处理这个问题。因此,在使用数组第一个元素的值之前,您必须先解开可选值。
现在,关于仅存在于块内的未包装值的问题if let
。想象一下并行代码,检查数组计数。会是一样的,对吧?
if a.count > 0 {
// use a[0]
}
// outside the block, no guarantee
// a[0] is valid
if let firstElement = a.first {
// use firstElement
}
// outside the block, you _can't_
// use firstElement
当然,如果计数为零,您可以执行诸如从函数中提前返回之类的操作。这可行,但有点容易出错 - 如果您忘记执行此操作,或者将其放入没有运行的条件语句中怎么办?本质上你可以做同样的事情array.first
:在函数中尽早检查计数,然后再执行array.first!
。但那个!
就像向您发出的信号 – 请注意,您正在做一些危险的事情,如果您的代码不完全正确,您会感到抱歉。
可选项还有助于使替代方案变得更漂亮。假设您希望在数组为空时使用默认值。而不是这个:
array.count > 0 ? a[0] : somedefault
你可以这样写:
array.first ?? somedefault
这在几个方面都更好。它将重要的事情放在前面:您想要的值是表达式的开始方式,后面是默认值。与三元表达式不同,它首先使用检查表达式,然后是您实际想要的值,最后是默认值。它也更加万无一失——更容易避免拼写错误,并且该拼写错误不可能导致运行时爆炸。
再举一个例子:find
功能。这会检查某个值是否在集合中并返回其位置的索引。但该价值可能不存在于收藏中。其他语言可能通过返回结束索引(它不指向某个值,而是指向最后一个值之后的一个)来处理此问题。这就是所谓的“哨兵”值——一个看起来像常规结果但实际上具有特殊含义的值。与前面的示例一样,在使用它之前,您必须检查结果是否不等于结束索引。但你必须know去做这个。你必须查找文档find
并确认它是如何工作的。
With find
返回一个可选的,当您理解可选的习惯用法时,很自然地意识到它这样做的原因是因为结果可能由于明显的原因而无效。上面提到的所有关于安全的事情也适用——你不能意外忘记,并使用结果作为索引,因为你必须先解开它。
也就是说,您可能会过度使用选项,因为它们是检查的负担。这就是为什么数组下标不返回可选值 - 它会产生很多麻烦,必须不断检查和解开它们,特别是当您知道您正在使用的索引是有效的时(例如,您在人们将使用的在数组的有效索引范围上的 for 循环!
不断地,从而使他们的代码变得混乱而没有任何好处。但随后添加了诸如first和last之类的辅助方法,以涵盖人们确实希望快速执行操作而不必先检查数组大小但又希望安全地执行操作的常见情况。
(另一方面,Swift 字典预计会通过无效下标定期访问,这就是为什么它们的[key]
方法确实返回一个可选的)
如果能够完全避免失败的可能性就更好了。例如,当filter
不匹配任何元素,它不返回nil
选修的。它返回一个空数组。 “嗯,显然会的”,你可能会说。但是你会惊讶地发现有人将返回值(如数组)设置为可选,而实际上他们应该返回一个空值。所以你说你应该避免选择,除非它们是必要的,这是完全正确的——这只是一个必要意味着什么的问题。在上面的例子中,我想说它们是必要的,并且是替代方案的更好解决方案。