在Swift中处理非可选选项

可选参数可以说是Swift最重要的功能之一,也是将其与Objective-C之类的语言区分开来的关键。 通过被迫处理可能 nil ,我们倾向于编写更具可预测性和较少错误的代码。

但是,有时可选参数会使您陷入困境,因为程序员如您所知(或者至少您是在这种假设下 ),即使使用某个变量,某些变量也总是非零。可选类型。 就像在视图控制器中处理视图时一样:

在这种情况下,Swift程序员将在几乎与制表符和空格相同的程度上存在分歧 有人说:

“由于它是可选的,因此您应该始终使用what或guard let适当地解开它。”

而其他人会朝完全不同的方向说:

“由于您知道该变量不会为nil,请强制将其解包(使用!)。 崩溃比最终处于不确定状态要好。”

基本上,我们在这里谈论的是是否进行防御性编程。 我们是否试图从不确定的状态中恢复,还是仅仅放弃并崩溃?

如果我不得不对这个问题给出一个二进制答案,那么我绝对会选择后者。 未定义状态会导致很难发现错误,可能会导致不必要的代码执行,而采用防御性编程只会导致难以推理的代码。

但是,我宁愿不必给出二进制答案,而是研究一些可用于以更细微的方式解决此问题的技术。 让我们潜入吧!

真的可选吗?

变量和属性是可选的,但实际上是程序逻辑所必需的,实际上是体系结构缺陷的征兆。 如果需要某些东西,直到没有它会使您处于未定义状态–它不应是可选的。

尽管在某些情况下(例如与某些系统API进行交互时),确实很难避免使用可选选项-在许多情况下,我们可以使用某些技术来摆脱可选选项。

懒惰胜于非可选的

一种避免使用属性的可选选项的方法是通过使用惰性属性,该属性需要在创建父对象之后创建值(例如,视图控制器中的视图,应在loadView()viewDidLoad() )。 惰性属性可以是非可选的,但仍不需要在其父级的初始化程序中创建。 它将在首次访问时创建。

让我们从之前更新TableViewController ,以为其tableView使用惰性属性:

没有可选项,没有不确定的状态! 🎉

适当的依赖项管理比非可选的可选项更好

可选项的另一个常见用法是打破循环依赖关系。 有时您会遇到A依赖于B ,但B也依赖于A 。 像这样的设置:

正如我们在上面看到的,我们在UserManagerCommentManager之间具有循环依赖关系,其中它们都不假定彼此拥有所有权,但是它们在逻辑上仍然CommentManager 。 那只是等待发生的错误! 😅

为了解决上述问题,我们改为让CommentComposer充当中间人,并负责通知UserManagerCommentManager已经作出评论:

这样, UserManager可以保留对CommentManager的强引用,而无需任何保留(或依赖)周期:

我们再次删除了所有可选内容,并提供了可预测的代码! 🎉

优雅地崩溃

在上面的示例中,我们看到了一些示例,在这些示例中,我们可以调整代码以通过删除可选项来消除不确定性。 但是,有时这是不可能的。 假设您正在加载包含应用程序配置的本地JSON文件。 这本质上是一个可能失败的操作,因此我们将需要添加一些错误处理。

如果配置无法加载,则继续执行程序会使应用程序处于未定义状态,因此在这种情况下最好崩溃。 这样,我们可以获得崩溃报告,并希望我们的测试和质量检查流程能够早于解决此问题并将其送达用户。

那么,我们如何崩溃? 最简单的解决方案是简单地使用! operator ! operator ,强制拆开可选选项,如果其中包含nil则会导致崩溃:

尽管这种方法很简单,但缺点是很大。 如果此代码开始崩溃,我们将得到的错误消息是:

 致命错误:解开Optional值时意外发现nil 

错误消息没有告诉我们错误的原因和发生位置,也没有提供有关解决方法的任何线索。 相反,让我们结合使用guard语句和preconditionFailure()函数,以自定义消息退出。

使用以上方法崩溃时,我们将获得更多有用的错误消息:

 致命错误:无法加载配置。 验证Config.JSON是否有效。:文件/Users/John/AmazingApp/Sources/AppDelegate.swift,第17行 

现在,我们可以采取一个明确的措施来解决该问题,并且我们确切知道它在代码库中的位置! 🚀

需求介绍

进行上面的guard-let-preconditionFailure舞蹈可能会有些乏味,并且确实会使代码难于遵循。 我们真的不想在代码中留出如此大的空间,而是要专注于逻辑。

我的解决方案是Require 。 它在Optional上添加了一个简单的require()方法,可完成上述操作,但使调用站点更加整洁。 使用Require时,上述配置加载代码如下所示:

如果失败,它将给我们以下错误消息:

 严重错误:必需值为零。 调试提示:验证Config.JSON是否有效:文件/Users/John/AmazingApp/Sources/AppDelegate.swift,第17行 

Require的另一个优点是,它还将引发NSException以及调用preconditionFailure ,这将使崩溃报告工具(如Crashlytics)能够提取崩溃的所有元数据。

如果您想在代码中使用Require,它是GitHub上的开源代码。

摘要

综上所述,这些是我在Swift中处理非可选可选内容的技巧:

  1. 懒惰胜于非可选的
  2. 适当的依赖项管理比非可选的可选项更好
  3. 当您需要使用非可选的可选选项时,正常崩溃

谢谢阅读! 🚀