【问题标题】:Swift 2: Call can throw, but it is not marked with 'try' and the error is not handledSwift 2:调用可以抛出,但它没有用'try'标记并且错误没有处理
【发布时间】:2015-08-24 13:54:04
【问题描述】:

在我安装 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 _ {
        }

    }

快照:

【问题讨论】:

    标签: ios xcode swift


    【解决方案1】:

    您必须捕获错误,就像您已经为 save() 调用所做的那样,并且由于您在此处处理多个错误,您可以在单个 do-catch 块中按顺序进行 try 多个调用,就像这样:

    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 在下面的 cmets 中指出的那样,最好不要在引发错误的地方捕获错误。您可以将该方法标记为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()
    }
    

    【讨论】:

    • 这有助于我弄清楚,谢谢。
    • 其实这里不需要捕获异常。可以只在函数调用中添加try 关键字,并将此函数声明为func deleteAccountDetail() throw。或者,如果您保证函数不会针对给定的输入抛出异常,您可以使用try!
    • 我提出这个不是为了挑剔,而是因为它实际上对基于异常的正确处理非常重要,大多数发生异常的地方都不会捕获异常。适合捕获异常的地方有 3 种。在所有其他地方,代码不应显式处理异常,而应依赖隐式的 deinit() 调用来进行清理(即 RAII),或偶尔使用 defer 进行一些临时清理。有关更多信息,请参见 exceptionsafecode.com(它讨论了 C++,但基本原则也适用于 Swift 异常。)
    • 但是你将如何运行这个函数呢?如果我选择@bames53 方式?
    • @NickMoore Swift 开发人员选择如何称呼它们并不会影响它们的实际名称。 Swift 的新错误处理系统是异常的一种实现,因为该术语在整个行业中都普遍使用。
    【解决方案2】:

    在 Swift 中调用使用throws 声明的函数时,必须使用trytry! 注释函数调用位置。例如,给定一个抛出函数:

    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 语法和错误处理模型的详细信息,请参阅书籍The Swift Programming Language (Swift 2 Prerelease)

    【讨论】:

    • 基本上catch自己能处理错误?或输入函数
    • @BrianS 我不确定你到底在问什么,尤其是关于“输入函数”,但在异常情况下,“catch”本质上是“处理”的同义词。也就是说,就编程语言而言,捕获异常和处理异常是一回事。
    • 我有一个错误我很不明白,你能帮帮我吗? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
    • @BrianS 听起来您在某处使用的函数签名不正确。期望给某些东西一个以NSData?, NSURLResponse?, NSError? 作为参数的函数,但你给它一个不带任何参数的函数。
    • 或者某些东西期望一个未声明的函数抛出异常,而你给它一个确实抛出异常的函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 2023-03-21
    相关资源
    最近更新 更多