Swift 2:通话可以抛出,但未标记为“ try”且未处理错误


161

在安装Xcode 7 beta并将Swift代码转换为Swift 2之后,我遇到了一些我无法弄清楚的代码问题。我知道Swift 2是新的,所以我搜索并找出了答案,因为没有任何相关内容,我应该写一个问题。

这是错误:

可以抛出呼叫,但未将其标记为“ try”,并且未处理错误

码:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

快照: 在此处输入图片说明

Answers:


168

您必须像save()处理呼叫一样捕获错误,并且由于您要在此处处理多个错误,因此可以try在一个do-catch块中依次进行多个调用,如下所示:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

或如@ bames53在下面的注释中指出的,通常最好的做法是不要在引发错误的地方捕获错误。您可以将方法标记为,throws然后try调用该方法。例如:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

这可以帮助我弄清楚,谢谢。
Farhad

5
实际上,不需要在这里捕获异常。可以仅将try关键字添加到函数调用中,并将此函数声明为func deleteAccountDetail() throw。或者,如果您保证该函数不会为给定的输入抛出,则可以使用try!
bames53 2015年

4
我不打算提起nitpick,而是因为它对于体面的基于异常的错误处理非常重要,因为大多数发生异常的地方都不会捕获异常。可以在三种情况下捕获异常。在所有其他地方,代码不应显式处理异常,而应依靠隐式deinit()调用进行清理(即RAII),或偶尔用于defer进行临时清理。有关更多信息,请参见exceptionsafecode.com(它讨论C ++,但是基本原理也适用于Swift异常。)
bames53 2015年

但是,您将如何运行该功能?如果我采用@ bames53的方式?
Farhad

1
@NickMoore Swift开发人员选择调用它们的名称并不会改变它们的实际外观。Swift的新错误处理系统是异常的实现,因为该术语在整个行业的其余部分中都广泛使用。
bames53

41

调用throws在Swift中声明的函数时,必须使用try或注释函数调用站点try!。例如,给定一个throwing函数:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

这个函数可以这样称呼:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

在这里,我们用注释调用try,它向读者表明该函数可能会引发异常,并且以下任何代码行均可能不会执行。我们还必须使用注释该函数throws,因为该函数可能会引发异常(即,当willOnlyThrowIfTrue()引发异常时,foo它将自动向上引发异常)。

如果您要调用一个声明为可能抛出的函数,但由于给您正确的输入而知道不会抛出该函数,则可以使用try!

func bar() {
  try! willOnlyThrowIfTrue(false)
}

这样,当您确保不会抛出代码时,就不必放入额外的样板代码来禁用异常传播。

try!是在运行时强制执行的:如果使用try!并且函数最终抛出,则程序的执行将因运行时错误而终止。

大多数异常处理代码应类似于上面:要么在发生异常时简单地向上传播异常,要么设置条件以排除可能的异常。对代码中其他资源的任何清理都应通过对象销毁(即deinit())或有时通过defered代码来进行。

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

如果您出于某种原因清理了需要运行但不在deinit()函数中的代码,则可以使用defer

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

大多数处理异常的代码只是简单地将它们向上传播给调用者,并通过deinit()或进行清除defer。这是因为大多数代码都不知道如何处理错误。它知道出了什么问题,但是没有足够的信息来了解一些更高级别的代码正在试图做什么,以便知道如何处理该错误。它不知道向用户显示对话框是否合适,或者是否应该重试,或者其他合适的方法。

但是,更高级别的代码应该确切知道在发生任何错误的情况下该怎么做。因此,异常允许特定错误从最初发生的地方冒出来,直到可以处理的地方。

处理异常是通过catch语句完成的。

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

您可以有多个catch语句,每个语句都捕获不同类型的异常。

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

有关例外的最佳做法的更多详细信息,请参见http://exceptionsafecode.com/。它专门针对C ++,但是在研究了Swift异常模型之后,我相信这些基础知识同样适用于Swift。

有关Swift语法和错误处理模型的详细信息,请参见《 Swift编程语言(Swift 2预发行版)》


基本上抓住自己可以处理的错误?或输入功能
Farhad 2015年

1
@BrianS我不确定您要问的是什么,尤其是关于“输入函数”的问题,但是在异常情况下,“ catch”本质上是“ handle”的同义词。也就是说,就编程语言而言,捕获异常和处理异常是同一回事。
bames53 2015年

我有一个错误,我很安静,不明白,您能帮我吗?Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad

@BrianS听起来您在某处使用了签名不正确的函数。某些东西期望被提供一个带有NSData?, NSURLResponse?, NSError?参数的函数,但是您给它一个不带任何参数的函数。
bames53 2015年

或某些期望未声明的函数引发异常,而您却给了它一个引发异常的函数。
bames53 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.