You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:32:57