在Swift中,如何避免可选项和零对象引用?

可选项的全部原因是防止由于命中分配给nil / null / none的variables导致运行时崩溃。 因此,variables不能为零; 相反,它们可以被包装在一个可选types中,表示它们为Some或None,并解包以获得Some或者nil的特定内容。

但是如果你把它们全部解开包装! ,或隐式解包选项,你只是介绍的可能性,因为你是一个不完善的编码器,运行时崩溃。 如果你使用if let来安全地打开它们,你可以避免崩溃,但是你被困在if let语句的范围之内来处理Some里面的值,而你仍然必须处理潜在的nil情况。 如果你使用? 为了调用方法暂时解包,在完成时会被重新包装,引入多层分层可选包装的混乱可能性。

所以:在我看来,要做的事情是避免可选项(除非需要时)(例如调用返回它们的框架方法)。 但是,如果我不使用可选项,这意味着我的对象引用必须是非零,我不知道如何处理的情况下,出于某种原因,不应该是或不是,一个分配给对象引用的值。

我的问题是:我如何避免需要零? 这似乎需要一个不同的编程方法。 (或者我应该只使用可选项,如果这就是我正在做的事情,那么和简单地将对象引用赋值为空如何与其他语言一样好?)

我知道这可能是一个主观的问题,但我应该问这个问题呢? 我并没有试图挑起或激起辩论,我真的很想知道什么是正确的方法,因为我写了更多的Swift代码。

你是对的,可选项可以是一个痛苦,这就是为什么你不应该过度使用它们。 但是,它们不仅仅是在使用框架时必须要处理的事情。 他们是一个难以置信的常见问题的解决scheme:如何处理一个返回结果可能是好的,也可能不会。

例如,获取Array.first成员。 这是一个方便的工具,它为您提供数组的第一个元素。 为什么能够调用a.first ,何时调用a[0]有用? 因为在运行时数组可能是空的,在这种情况下a[0]会爆炸。 当然,你可以事先检查a.count ,但是会再次检查

一个。 你可能会忘记,

湾 这会导致相当难看的代码。

Array.first通过返回一个可选项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 :在函数的早期检查count,然后再做array.first! 。 但那! 就像是给你的信号 – 要小心,你正在做一些危险的事情,如果你的代码不完全正确,你会后悔的。

select权也有助于使select稍微漂亮。 假设你想在数组为空时默认值。 而不是这个:

 array.count > 0 ? a[0] : somedefault 

你可以写这个:

 array.first ?? somedefault 

这在几个方面更好。 它把重要的事情放在前面:你想要的价值是expression如何开始,其次是默认值。 与三元expression式不同的是,它先检查expression式,然后是你真正想要的值,然后是默认值。 这也更加简单 – 避免input错误更容易,并且不可能导致运行时间爆炸。

再举一个例子: find函数。 这将检查值是否在集合中并返回其位置的索引。 但是这个价值可能不在collections中。 其他语言可能会通过返回结束索引(它不指向一个值,而是一个超过最后一个值)来处理这个问题。 这就是所谓的“哨兵”值 – 看起来像一个常规的结果,但实际上有特殊的意义。 像前面的例子一样,在使用结果之前,你必须检查结果是不是等于结束索引。 但是你必须知道要做到这一点。 你必须查找文件find并确认它是如何工作的。

使用find返回一个可选项,这是很自然的,当你理解可选的习惯用法时,要意识到它所做的原因是因为结果可能不是有效的。 而且上面提到的所有有关安全的事情也都适用 – 你不能不小心忘记,并将结果用作索引,因为你必须首先打开它。

也就是说,你可以过度使用可选项,因为它们是必须检查的负担。 这就是为什么数组下标不会返回可选项 – 这会造成很多麻烦,因此必须经常检查并解包它们,尤其是当您知道您使用的索引是有效的(例如,您处于一个for循环数组的有效索引范围),人们将使用! 不断,从而混乱他们的代码没有好处。 但是,像第一个和最后一个这样的辅助方法被添加来覆盖常见的情况,人们希望快速执行操作,而不必首先检查数组大小,但要安全地进行操作。

(Swift字典,另一方面,预计会定期访问通过无效的下标,这就是为什么他们[key]方法确实返回一个可选)

更好的是,如果可以完全避免失败的可能性。 例如,当filter匹配任何元素时,它不返回一个可选的nil 。 它返回一个空数组。 “很明显,它会”,你可能会说。 但是你会惊奇地发现,如果有人像数组一样返回一个返回值,那么实际上他们只是应该返回一个空的数组。 所以你完全正确地说,除非必要,否则应该避免可选项 – 这只是一个必要的问题。 在上面的例子中,我会说他们是必要的,并且是更好的解决scheme。

或者,我应该只使用可选项,如果这就是我正在做的事情,它是如何比其他语言具有空对象引用空分配好?

如果你的计算可能需要返回一个“特殊”的值,那么是的,在Swift中,你应该使用可选项。 它们比空的types更好,因为它们是明确的 。 很容易错过一个指针可能nil的情况,使用可选项更困难(但完全有可能)。

如果使用“如果让”来安全地解开它们,那么就避免了崩溃,但是你被困在“if let”语句的范围之内来处理这个值,而你仍然需要处理这个潜在的零案例。

这是一个function。 我的意思是,这是一个可选types的重点:你必须处理这两种情况( nil和非零),你必须明确这一点。

有关类似概念的示例,请参见Haskell的Maybetypes和monad 。 Maybetypes与可选types完全等价,Maybe monad使用这些可选值很容易“链接”操作,而不必一直手动检查空值。