Swift 4中'void函数返回非void值'错误求助
解决Swift中"unexpected non-void return value in void"错误的方案
嘿,我来帮你搞定这个返回值错误的问题!你遇到的核心问题其实是在一个无返回值(void)的上下文里,尝试返回String类型的值——哪怕你已经给gotourl()声明了返回String,也没用,因为问题出在你代码里的switch语句大概率是写在SwiftECP的异步登录闭包里面,而这个闭包本身是无返回值的,和外层gotourl()的返回声明没关系。
为什么会报错?
举个例子,你可能之前的代码是类似这样的:
func gotourl() -> String { ECPLogin.login(username: "...", password: "...") { response, error in switch response { case .success(let body): return body // 这里就会报错!因为这个闭包是() -> Void类型,不允许返回值 // 其他分支... } } // 而且这里还有问题:异步请求还没完成,函数就已经走到这里了,根本没法返回正确的值 }
编译器看到你在无返回值的闭包里写了return body,就会抛出"unexpected non-void return value in void"的错误。另外,网络请求是异步的,外层函数gotourl()会在请求完成前就执行完毕,直接返回值的写法本身逻辑就有问题。
两种可行的解决方案
方案1:用闭包回调传递结果(兼容所有Swift版本)
把gotourl()改成接受一个完成回调的函数,在登录成功/失败的分支里调用回调传递结果,而不是直接返回值:
import UIKit import SwiftECP import XCGLogger class ViewController: UIViewController { @IBOutlet var UsernameField: UITextField! @IBOutlet var passwordField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func _Login(_ sender: UIButton) { // 调用gotourl,通过闭包接收登录结果 gotourl { result in switch result { case .success(let responseBody): print("登录成功!响应体:\(responseBody)") // 这里可以写登录成功后的逻辑,比如跳转页面 case .failure(let error): print("登录失败:\(error.localizedDescription)") // 处理错误,比如提示用户 } } } // 改写gotourl,使用完成回调代替直接返回 func gotourl(completion: @escaping (Result<String, Error>) -> Void) { guard let username = UsernameField.text, !username.isEmpty, let password = passwordField.text, !password.isEmpty else { completion(.failure(NSError(domain: "LoginError", code: 0, userInfo: [NSLocalizedDescriptionKey: "用户名或密码不能为空"]))) return } // 这里根据SwiftECP的实际登录API调整,假设是带回调的方法 ECPLogin.login(username: username, password: password) { response, error in if let error = error { completion(.failure(error)) return } guard let responseBody = response?.body else { completion(.failure(NSError(domain: "LoginError", code: 1, userInfo: [NSLocalizedDescriptionKey: "未获取到响应内容"]))) return } // 把响应体通过回调传出去 completion(.success(responseBody)) } } }
方案2:用async/await简化异步逻辑(iOS 15+ / Swift 5.5+)
如果你的项目支持iOS15及以上,可以把SwiftECP的回调式API包装成async/await风格,这样就能像写同步代码一样返回值了:
import UIKit import SwiftECP import XCGLogger class ViewController: UIViewController { @IBOutlet var UsernameField: UITextField! @IBOutlet var passwordField: UITextField! override func viewDidLoad() { super.viewDidLoad() } @IBAction func _Login(_ sender: UIButton) { // 在Task里执行异步代码 Task { do { let responseBody = try await gotourl() print("登录成功!响应体:\(responseBody)") // 后续逻辑 } catch { print("登录失败:\(error.localizedDescription)") } } } // 把gotourl改成async throws函数 func gotourl() async throws -> String { guard let username = UsernameField.text, !username.isEmpty, let password = passwordField.text, !password.isEmpty else { throw NSError(domain: "LoginError", code: 0, userInfo: [NSLocalizedDescriptionKey: "用户名或密码不能为空"]) } // 调用包装好的async登录方法 return try await ECPLogin.loginAsync(username: username, password: password) } } // 给SwiftECP扩展一个async风格的登录方法 extension ECPLogin { static func loginAsync(username: String, password: String) async throws -> String { return try await withCheckedThrowingContinuation { continuation in login(username: username, password: password) { response, error in if let error = error { continuation.resume(throwing: error) return } guard let body = response?.body else { continuation.resume(throwing: NSError(domain: "LoginError", code: 1, userInfo: [NSLocalizedDescriptionKey: "未获取到响应内容"])) return } continuation.resume(returning: body) } } } }
关键总结
- 别在异步闭包里直接写
return给外层函数,闭包的返回类型是独立的,和外层函数没关系; - 异步操作不能直接用同步返回值的方式处理,要么用回调,要么用async/await;
- 先检查你的switch语句是不是写在某个无返回值的闭包里面,这是报错的直接原因。
内容的提问来源于stack exchange,提问作者Shaa'ban Mego
相关产品推荐
相关产品推荐

