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

2023-12-02

选项的全部原因是为了防止由于命中分配给 nil/null/none 的变量而导致运行时崩溃。因此,变量不能为nil;相反,它们可以包装在将它们表示为 Some 或 None 的可选类型中,并展开以获取 Some 或 nil 的特定内容。

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

所以:在我看来,要做的事情是避免可选,除非有必要(例如,当调用返回它们的框架方法时)。但是,如果我不使用可选值,这意味着我的对象引用必须为非零,并且我无法弄清楚如何处理由于某种原因不应该存在或不存在的情况,分配给对象引用的值。

我的问题是:如何避免需要 nil?看起来它需要不同的编程方法。 (或者我应该只使用可选值,如果这就是我正在做的,那么它比像其他语言那样简单地对对象引用进行空赋值有什么好处?)

我知道这可能是一个主观问题,但我还能在哪里问这个问题呢?我并不是想挑衅或挑起争论,我真的很想知道当我编写更多 Swift 代码时正确的方法是什么。


你是对的,选项可能会很痛苦,这就是为什么你不应该过度使用它们。但它们不仅仅是使用框架时必须处理的事情。它们是一个非常常见的问题的解决方案:如何处理返回可能正常或可能不正常结果的调用。

例如,采取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选修的。它返回一个空数组。 “嗯,显然会的”,你可能会说。但是你会惊讶地发现有人将返回值(如数组)设置为可选,而实际上他们应该返回一个空值。所以你说你应该避免选择,除非它们是必要的,这是完全正确的——这只是一个必要意味着什么的问题。在上面的例子中,我想说它们是必要的,并且是替代方案的更好解决方案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Swift 中,如何避免可选值和 nil 对象引用? 的相关文章

随机推荐

  • 向电话号码添加自定义字段

    正在构建一个应用程序 需要为每个电话号码提供自定义联系人字段 白名单 如果您想知道 但是 我只找到了一种为每个联系人保存自定义数据的方法 Data RAW CONTACT ID 但不是每个电话号码 我尝试使用Phone id 但我得到了一个
  • Laravel SQL Server 连接 ENCRYPT=yes trustServerCertificate=true

    我有一个运行 php 5 5 9 laraverl 5 2 的 ubuntu docker 容器 它可以成功连接到 SQL Server 并获取结果 我正在使用的docker镜像是https hub docker com r h2labs
  • $sf_response->addStyleSheet() 在 SF 1.4 中不起作用?

    有谁知道如何使用 Symfony 1 4 在模板中添加样式表 我已经尝试了我能想到的一切 从修改 frontend config view yml 到修改模板本身 两者都有效 我从我的搜索中看到 其他人也遇到了同样的问题 使用 includ
  • TIdServerIOHandlerSSLOpenSSL.SSLOptions.CipherList 中密码名称的顺序重要吗?

    我将 Web 服务允许的密码限制为仅以下 TLS 1 x 密码 TLS RSA WITH AES 128 CBC SHA TLS RSA WITH AES 128 CBC SHA256 TLS RSA WITH AES 128 GCM SH
  • VS2008 在遇到断点时挂起所有线程

    我一定是错过了一些东西 我习惯使用eclipse 当我遇到断点时 Visual Studio 2008 似乎会挂起所有线程 这正常吗 如果是这样 是否有一个选项只会挂起断点所在的线程 是的 这对于调试器来说是完全正常的 考虑一下如果不这样做
  • 从 GNU 并行调用 GNU 并行

    嵌套调用的正确方法是什么GNU parallel 愚蠢的例子 seq 1 100 parallel echo parallel seq 1000 我的理解是 在 8 个 CPU 的机器上 每个并行将启动 8 个作业 总共 64 个作业 如果
  • 如何使用 httpPost 将数据发送到网站,应用程序崩溃[重复]

    这个问题在这里已经有答案了 目前我正在做一个项目 我需要将一些数据从我的 Android 应用程序发送到网络服务器 但是当我触摸发送按钮时 应用程序崩溃了 这是我的 java 文件 package com androidexample ht
  • Android:调用 getFilter() 时更改 ListView 中的 CheckBox 检查值

    我最近posted我的问题MultiSpinner值以从微调器对话框中选择多个值 我已经成功完成了接下来的课程 MultiSpinnerSearch java package com example multiplechoicelistwi
  • “文件”对象没有属性“rfind”[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 目前不接受答案 我正在尝试将 word2vec 保存到文件中 model Word2Vec sentences size vector size window 5 min count 5 w
  • 在 ASP.NET 中使用通用处理程序对 jQuery AJAX 调用进行故障排除

    使用 ASP NET 对 jQuery AJAX 调用进行故障排除的最佳方法是什么 错误函数被调用 我收到弹出窗口的警报 显示 错误 但我不知道为什么 当我附加到我的进程 w3wp exe 并将断点放置到我的通用处理程序 ProcessRe
  • 试图弄清楚如何在 c 中输出电话号码?

    我正在尝试输入电话号码并以 888 999 1111 格式打印出来 但是当我尝试打印出来时 我得到了一些奇怪的输出 这不是我期望得到的输出 我在输入和打印功能中打印出电话的值 但它们是不同的 输入函数中的正确 但打印函数中的错误 先谢谢您的
  • MongoDB 集合中的唯一文档

    我需要在文档中存储三个 id 但它们只能出现一次 例如 以下文档在此集合中只能出现一次 user ObjectId j8uwh902w5489 comment ObjectId 09890583457jkjsf4 whatever Obje
  • 使用 Sceneform Android 在 3D 模型上绘制可渲染形状

    我在用场景窗体 Android SDK在Android APP中渲染3D模型 为了显示 3D 模型 我使用下面的代码 它也支持 3D 模型中的旋转和缩放功能 private fun renderLocalObject position In
  • Mac OS X Cocoa,在全局屏幕坐标空间中翻转 Y 坐标

    如何将 NSPoint 的 y 值翻转到翻转的坐标空间 我有两个点源 都在全局屏幕空间中 但其中一个是翻转的 一个来自屏幕坐标空间 0 0 左上角 一个来自屏幕坐标空间 0 0 左下角 我需要翻转第二个 左下角 使其与第一个 左上角 处于同
  • 无法确定关联的主要目的

    首先使用 EF5 代码 我有两个类 Table UserProfile public class UserProfile Key DatabaseGenerated DatabaseGeneratedOption Identity publ
  • JOIN 查询的 Php pdo 结果

    我的问题是 我尝试在两个表之间进行简单的 JOIN 这两个表都有 id 字段 我的结果是一个 stdClass 对象 因为我使用 PDO 有谁知道如何区分第一个表的 id 和第二个表的 id Code sql SELECT FROM pro
  • Swift 中的 CLGeocoder - 使用verseGeocodeLocation 时无法返回字符串

    我正在尝试使用 CLGeocoder 返回字符串中坐标的位置 我的代码目前如下所示 func getPlaceName latitude Double longitude Double gt String let coordinates C
  • Firebase 身份验证电子邮件自定义

    我在我的应用程序中使用 firebase auth 并且正在设置无密码电子邮件注册 我已设法从自己的域设置电子邮件 但如何更改电子邮件中发送的魔术链接文本 我可以看到其他模板电子邮件的配置 但看不到这封电子邮件的配置 有问题的电子邮件是这样
  • 获取所有行都为 true 的 id (sqlalchemy)

    我试图找到一种简单的方法来查找特定的表单或表单 其中所有项目都带有form id是活跃的 这是我的Messages table class Messages db Model tablename Messages id db Column
  • 在 Swift 中,如何避免可选值和 nil 对象引用?

    选项的全部原因是为了防止由于命中分配给 nil null none 的变量而导致运行时崩溃 因此 变量不能为nil 相反 它们可以包装在将它们表示为 Some 或 None 的可选类型中 并展开以获取 Some 或 nil 的特定内容 但如