Swift的“可选项”为什么比Objective-C的“nil”更安全?

13

苹果似乎声称,Swift中的Optional类型比Objective-C中的nil更安全,但我不明白为什么。

实现上的主要区别是什么,使得Optionalnil更安全,这会如何影响我的代码?


10
Swift不支持在使用Optional之外的情况下使用nil。Optional可以包含nil,而非Optional则不能包含nil。如果你使用“!”将变量声明为隐式解包变量,并且当你使用它时该变量包含了nil,则会引发异常。因此,在你不希望出现变量“神奇”地包含nil的情况下,你不必处理这种情况。 - Michael
1
@Michael,你的评论就是一个答案。 - Dmitri Pavlutin
4个回答

2
它变得“安全”是因为它强制程序员编写更明确的代码。
  • if let ...guard 语法明确表示“只有这些变量有值时才执行此操作”。
  • ! 语法会使程序在变量为 nil 时崩溃,而程序员并未预期该变量为 nil
相比之下,Objective-C 的调用 nil 的方式可能会让程序继续执行,直到 nil 不再可接受为止。一个常见的例子是 nilNSString 变量。许多 API 调用都可以接受 nilNSString,除了构造 NSAttributedString(它不会接受 nilNSString 参数并且会导致应用程序崩溃)。因此,当你意外地得到一个 NSString 变量变成了 nil,你的应用程序可能会在一段时间后崩溃,因为它尝试使用该 nil 值构造属性字符串。
要了解 Optionals 尝试解决的问题的更完整说明,请参考 Daniel Jalkut 在 2010 年撰写的这篇文章。这是在 Swift 之前写的。

2

一般来说,在编程中,我们希望避免没有值的变量(nullnil),因为使用它们通常会导致未定义的行为(异常、错误、崩溃)。

例如,常见做法是将Array引用设置为空数组而不是nil。在空数组上,我们可以使用所有的Array方法,例如indexOfcount。在nil上使用这些方法会导致崩溃。

可选项允许我们指定某些变量永远不为空,因此我们可以安全地使用它们,并且编译器检查nil永远不会分配给它们。此外,编译器强制执行每个从可选类型转换为非可选类型的显式转换(我们始终在需要时检查nil)。

因此,答案是可选项:

  1. 强制执行良好的编程实践。
  2. 允许更好地在编译时检查代码

从而防止编程错误。

还要注意的是,尽可能避免使用可选项。可选项最强大的功能是大多数变量都不是可选项。


你能详细说明一下你上次说的“可选项最大的优势在于大多数变量都不是可选项”这句话吗? - Joe Huang
@JoeHuang 这很简单。如果你的所有变量都是可选类型,那么你就回到了没有可选类型的世界。一切都可以是 nil。可选类型的威力在于将 nil 限制在特定的库中。 - Sulthan
在 ObjC 中,使用 nil 上的 [indexOfObject:count] 会导致崩溃。 - jscs
比较Objective-C和Swift:在Objective-C中,在空数组上调用数组方法不会崩溃。indexOfObject返回0,count返回0,诸如此类。 - gnasher729
@JoshCaswell 在 Obj-C 中,调用不会崩溃,但结果仍然是意外的。例如,[nil indexOfObject:x] 不会返回 NSNotFound。这就是我试图说明的重点。有些情况下语言是幸运的,比如 string.length == 0array.count == 0,但有很多情况下应用程序不会崩溃,但会导致意外状态。在 SO 上最常见的 Obj-C 问题之一是:“我的方法没有被调用。”。 - Sulthan
显示剩余2条评论

2

为了避免问题,我经常在使用变量之前检查它是否为nil。然而,偶尔会出现值作为nil的情况,让我感到惊讶——有时候是我自己忘记检查。

随着我在Swift中编写越来越多的代码,我发现使用可选变量确实提供了很多便利,避免忽略对nil值的检查。我不太记得有任何由于nil变量访问而导致的崩溃(除非我强制解包某个变量;因此,除非您知道自己在做什么,否则不要强制解包)。

此外,我曾经在许多地方编写过很多像这样的代码:

if (obj != nil) {
    obj.property = ...
}

现在,我可以简单地这样做:
 obj?.property = ...

代码现在看起来很干净、锐利,你也喜欢吗?

1
这是一个相当糟糕的例子,因为在Objective-C中,obj.property = ...可以正常工作但什么也不做。 - gnasher729
我不是在指Objective-C。这只是大多数编程语言的一般情况。例如,尝试在Lua中执行此操作。崩溃了! - Joe Huang
这个问题已经被更改了。当我回答这个问题时,我认为这个问题没有提到Objective-C的nil或者比较Swift和Objective-C。这个问题必须被合并或者更改。它已经失去了原来的意图。 - Joe Huang

-2
在Objective-C中,你可以定义一个没有值的变量。如果你想在赋值之前使用该变量,那么你将会得到一个未定义的行为(这是非常糟糕的)。相反,Swift会防止对象值不明确的情况发生。

1
自从几年前引入ARC后,任何指向Objective-C对象的指针都会自动初始化为nil,避免了未定义的行为。如果您开启了合理的编译设置,那么如果尝试使用未初始化的变量,您的代码将无法编译通过。 - gnasher729

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接