【问题标题】:Can't get throws to work with function with completion handler无法使用带有完成处理程序的函数进行抛出
【发布时间】:2015-10-28 21:58:51
【问题描述】:

我正在尝试使用完成处理程序将throws 添加到我现有的函数中,但我不断收到警告说no calls throwing functions occur within try expression。在我抛出错误的部分,我收到一条错误消息

从 '() throwing -> Void' 类型的抛出函数到非抛出函数类型的无效转换。

enum LoginError: ErrorType {
    case Invalid_Credentials
    case Unable_To_Access_Login
    case User_Not_Found
}

@IBAction func loginPressed(sender: AnyObject) {

    do{
        try self.login3(dict, completion: { (result) -> Void in

            if (result == true)
            {
                self.performSegueWithIdentifier("loginSegue", sender: nil)
            }
        })
    }
    catch LoginError.User_Not_Found
    {
        //deal with it
    }
    catch LoginError.Unable_To_Access_Login
    {
        //deal with it
    }
    catch LoginError.Invalid_Credentials
    {
        //deal with it
    }
    catch
    {
        print("i dunno")
    }

}

func login3(params:[String: String], completion: (result:Bool) throws -> Void)
{
    //Request set up
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary
            if let parseJSON = json
            {
                let userID = parseJSON["user_id"] as? Int
                let loginError = parseJSON["user_not_found"] as? String
                let validationError = parseJSON["invalid_credentials"] as? String
                let exception = parseJSON["unable_to_access_login"] as? String

                var responseArray = [(parseJSON["user_id"] as? Int)]
                if userID != nil
                {
                    dispatch_async(dispatch_get_main_queue()) {
                        completion(result:true)
                    }

                }
                else if loginError != ""
                {
                    dispatch_async(dispatch_get_main_queue()){
                        completion(result: false)
                        self.loginErrorLabel.text = loginError
                        throw LoginError.User_Not_Found
                    }
                }
                else if validationError != ""
                {
                    dispatch_async(dispatch_get_main_queue()){
                        completion(result:false)
                        self.validationErrorLabel.text = validationError
                        throw LoginError.Invalid_Credentials
                    }

                }
                else if exception != nil
                {
                    dispatch_async(dispatch_get_main_queue()){
                        completion(result:false)
                        self.exceptionErrorLabel.text = "Unable to login"
                        throw LoginError.Unable_To_Access_Login
                    }
                }
            }
            else
            {
            }
        }
        catch let parseError {
            // Log the error thrown by `JSONObjectWithData`
        })

        task.resume()

}

【问题讨论】:

  • 这是一个异步函数。在您的回调中,您需要返回一个不抛出的错误,当您抛出时,您的程序已经从该函数返回,因此它不起作用。
  • 我有点困惑。我希望回调抛出还是包含回调的函数应该抛出?我正在等待服务器的响应以确定要抛出什么类型的错误,这让我认为回调需要抛出。
  • @Victor Sigler 答案就是您要找的。​​span>

标签: swift asynchronous error-handling swift2 try-catch


【解决方案1】:

您可以做的是将错误封装到一个可抛出的闭包中,如以下代码所示,以实现您想要的:

func login3(params:[String: String], completion: (inner: () throws -> Bool) -> ()) {

   let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in

            let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary

            if let parseJSON = json {
               let userID = parseJSON["user_id"] as? Int
               let loginError = parseJSON["user_not_found"] as? String
               let validationError = parseJSON["invalid_credentials"] as? String
               let exception = parseJSON["unable_to_access_login"] as? String

               var responseArray = [(parseJSON["user_id"] as? Int)]
               if userID != nil {
                 dispatch_async(dispatch_get_main_queue()) {
                     completion(inner: { return true })
                 }

            }
            else if loginError != ""
            {
                dispatch_async(dispatch_get_main_queue()) {
                    self.loginErrorLabel.text = loginError
                    completion(inner: { throw LoginError.User_Not_Found })
                }
            }
            else if validationError != ""
            {
                dispatch_async(dispatch_get_main_queue()) {
                    self.validationErrorLabel.text = validationError
                    completion(inner: {throw LoginError.Invalid_Credentials})
                }
            }
            else if exception != nil
            {
                dispatch_async(dispatch_get_main_queue()){
                    self.exceptionErrorLabel.text = "Unable to login"
                    completion(inner: {throw LoginError.Unable_To_Access_Login})
                }
            }
        }
        else
        {
        }
    }

   task.resume()
}

你可以这样称呼它:

self.login3(dict) { (inner: () throws -> Bool) -> Void in
   do {
     let result = try inner()
     self.performSegueWithIdentifier("loginSegue", sender: nil)
   } catch let error {
      print(error)
   }
}

诀窍在于login3 函数需要一个额外的闭包,称为'inner',类型为() throws -> Bool。这个闭包要么提供计算结果,要么抛出。闭包本身是在计算期间通过以下两种方式之一构建的:

  • 如果出现错误:inner: {throw error}
  • 如果成功:inner: {return result}

我强烈推荐你一篇关于在异步调用Using try / catch in Swift with asynchronous closures 中使用try/catch 的优秀文章

希望对你有所帮助。

【讨论】:

  • 如果你想将完成块中的登录错误抛给另一个函数,上述方法不起作用。
【解决方案2】:

你要X,我回答Y,但以防万一……

总有可能将抛出功能添加到您的函数而不是完成处理程序:

func login3(params:[String: String], completion: (result:Bool) -> Void) throws {
    ...
}

然后你可以从 IBAction 内部调用它:

do {
    try self.login3(dict) { result -> Void in
        ...
    }
} catch {
    print(error)
}

【讨论】:

  • 我尝试了您的方法,但遇到了一个错误。在dispatch_async(dispatch_get_main_queue()) { self.loginErrorLabel.text = loginError completion(inner: { throw LoginError.User_Not_Found }) },我仍然收到错误消息,指出从“() throwing -> Void”类型的抛出函数到非抛出函数类型的转换无效。
  • @Brosef 有了这个版本,无需更改您的完成,保持原样(没有“内在”的东西)。除了我所展示的,不需要其他任何东西。
  • 我正在尝试实施您的解决方案,但如果我将 throw 放在 dispatch_async 中,我会收到我在之前的评论中描述的错误。
  • 我没有弄清楚我的提议的所有含义,事实上,这只是对替代方案的暗示。 :) 无论如何,维克多的回答非常好,我会说他的解决方案,看起来它符合您的要求。
【解决方案3】:

用一粒盐阅读下面的内容。我还没有用 Swift 2.0 做太多工作:

您创建了一个“dataTask”task,它的完成处理程序中有一个 throw,但您的 login3 方法中唯一的实际代码是 task.resume()。直到login3 返回之后,完成处理程序才会被执行。 (事实上​​,它是另一个对象的参数,所以编译器不知道该代码会发生什么。)

据我了解,login3 方法的实际从上到下的主体必须包含一个 throw。由于它是异步方法,因此您不能这样做。因此,不要让你的 login3 函数抛出。而是让它将错误对象传递给它的完成处理程序。

【讨论】:

    【解决方案4】:

    在我的印象中,这是由您的函数签名引起的。在@IBAction func loginPressed(sender: AnyObject) 中,调用login3 时不必使用try,因为函数本身没有被标记为抛出,但完成处理程序被标记。但事实上,login3 的完成闭包永远不会抛出,因为您在 do {} catch {} 块内执行所有抛出函数,因此您可以尝试从 login3 完成闭包中删除 throws 注释并调用闭包 if你在login3 中发现了一个错误,并给出了相应的结果。这样,您可以在 do {} catch {} 块中处理 login3 内的所有抛出函数,并使用合适的值调用完成处理程序。

    一般来说,我也不知道您可以在没有前面的 do {} 块的情况下捕获,就像您在 @IBAction 中所做的那样。

    【讨论】:

    • login3 的抛出也是不必要的,因为您将直接在 catch 块中捕获先前抛出的错误。我建议检查错误条件并调用完成闭包,出现错误,或者在您的情况下使用 false 作为结果。如果您修改闭包参数以便可以将错误传递给闭包,那么您可以在 @IBAction 中检查不同的错误条件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-19
    • 2014-10-06
    • 2019-01-13
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    相关资源
    最近更新 更多