为什么要避免强行打开

时间:2017-10-12 17:27:37

标签: swift optional

有些情况下您忘记设置值(因此它实际上是一个错误),并且使用强制解包运行程序可能会导致问题崩溃,这可以让您找到忘记设置值的错误你应该设置的。

从关于避免强制解包的帖子开始,总是提出强制解包会导致程序崩溃,因此这是一件坏事。当问题真正出现问题时崩溃会有什么不妥?

请举例说明强制展开可能是坏事。

(我并不是说强行打开适合所有事情。)

3 个答案:

答案 0 :(得分:12)

只有当你作为程序员知道一个可选项永远不会是nil的事实时,才应该使用强制解包(我还要包括强制转换) nil代表了开发过程中代码中的一个明显错误(然后你希望它崩溃)。

有许多例子表明这种类型的强制展开是合适的。例子包括:

  • 获取应用包中已知文件的路径(nil表示您在开发过程中忘记定位文件)。
  • 强制拨打UITableView dequeueReusableCell的电话(无意味着您的故事板中有错误)。
  • 当你特意向Calendar询问该组件时,从DateComponents获取一个特定的组件(零意味着你有一个错字)。

显然还有很多其他情况需要强制解包,但你必须清楚地了解这些情况。

但是,有许多运行时决策会导致您无法保证选项,并且不应强制解包此类案例。

示例包括:

  • 处理任何用户输入。永远不要假设用户输入有效数据。永远不要假设值可以按预期转换。始终检查nil结果。
  • 解析JSON结果。永远不要假设您获得的数据符合某些预期的格式,即使该格式已明确记录并且似乎总是有效。事情随着时间而变化。优雅地处理这些意外数据,而不仅仅是假设一个值将始终存在于假定的数据类型中。
  • 处理可以throw或返回可选结果的任何API。事情可能会出错。错误发生。永远不要以为你会得到有效答案。代码防守。

最后,开发人员具有适当的经验和理解选项如何工作,他们的意思,以及何时值可能或可能不是nil的情况下的开发人员能够安全地使用强制解包适当。明智地使用它。

永远不要使用强制解包,因为Xcode建议让编译器满意。

答案 1 :(得分:1)

强制解包很糟糕,因为您的程序无法保证在执行时访问实际变量。当发生这种情况时,您的程序可能正在尝试对不存在的数字执行数学计算,并且您的应用程序将崩溃。在开发阶段,如果它崩溃了,你将能够缩小崩溃发生的原因,并在运行阶段确定它在运行时的问题,但在生产中呢? / p>

例如,如果您从Web服务检索某种数字,您可能希望将此数字与本地数字进行比较,可能是版本号:

if let json = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as? [String: Any], 
   let serverAPIVersion:NSNumber = json["API_Version_Number"] as? NSNumber {

    if self.currentAPIVersion.uintValue < serverAPIVersion.uintValue {
       self.updateVersionWith(number: serverAPIVersion)
    }

 }

在上面的代码中,我们安全地从我们从服务器获得的JSON中展开"API_Version_Number"。我们安全地展开,因为如果"API_Version_Number"没有值,那么当我们尝试与当前版本进行比较时,程序会崩溃。

// This will crash if the server does not include "API_Version_Number in json response data
let serverAPIVersion:NSNumber = json["API_Version_Number"] as! NSNumber

在生产中,有些事情是您无法控制的(很多时候是服务器端问题)可能会导致无人居住的变量。这就是为什么最好有条件地解包以在代码中安全地访问值,以防止在执行时崩溃。

答案 2 :(得分:0)

如果程序确实有错误,那么崩溃程序没什么问题...如果你无法解决崩溃问题

但正如@ phillip-mills在评论中所说:

  

......在没有理解导致崩溃的原因的情况下使用强制解包的人问道

我们经常会看到一些例子,人们强行打开选项,出于某些意想不到的原因,它们不存在,并且在这种情况下尝试打开可选的&#34;优雅。

不良强制展开的示例

假设您有一个后端服务,它为您提供了一些JSON,您可以将其解析为模型对象并显示在视图中。

假设你有一个像这样的模型:

struct Person {
    let firstName: String?
    let lastName: String?
}

UITableView中,您使用人物对象填充一些单元格。有点像:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //dequeue your cell
    //get your person element from some array
    //populate
    cell.firstNameLabel.text = person.firstName!
    cell.lastNameLabel.text = person.lastName! 

    return cell
}

在某些时候,你最终可能会得到一个没有Person的{​​{1}}(你可以说这是后端问题,依此类推等等......但是......&# 39; ll可能会发生:))如果您强行打开包装,您的应用程序将崩溃,因为它不应该是必要的。在这种情况下,你可以放弃优雅的展开

firstName

或者您可以使用 Nil Coalescing Operator

if let firstName = person.firstName {
    cell.firstNameLabel.text = firstName
}

最后,正如@rmaddy在回答中所说的那样

  

强制解包是不好的,因为您的程序无法保证在执行时访问实际变量

您无法始终确保数据符合您的预期。

如果你完全确定你所操作的数据是有效的,也就是说,如果你已经安全地打开它,你实际上只能做一些操作。 。愚蠢不使用该选项:)

希望对你有所帮助。