无法使用带有完成处理程序的函数进行抛出

Can't get throws to work with function with completion handler

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

invalid conversion from throwing function of type '() throwing -> Void' to non-throwing function type.

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()

}

请谨慎阅读以下内容。我还没有使用 Swift 2.0:

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

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

在我看来,这是由您的函数签名引起的。在 @IBAction func loginPressed(sender: AnyObject) 中,您不必在调用 login3 时使用 try,因为函数本身未标记为抛出,但完成处理程序是。但实际上 login3 的完成闭包永远不会抛出,因为你在 do {} catch {} 块中执行所有抛出函数,所以你可以尝试从 login3 中删除 throws 注释完成闭包,如果您在 login3 中发现错误并得到相应的结果,也会调用闭包。通过这种方式,您可以在 do {} catch {} 块中处理 login3 内的所有抛出函数,并使用合适的值调用完成处理程序。

通常我也不知道您可以像在@IBAction 中那样在没有前面的 do {} 块的情况下进行捕获。

你问的是 X,我回答的是 Y,但以防万一...

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

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

然后你可以在 IBAction 中调用它:

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

你可以做的是将错误封装到一个可抛出的闭包中,就像下面的代码一样,以实现你想要的:

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}

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

希望对你有所帮助。