You need to enable JavaScript to run this app. Swift开发:使用DispatchQueue实现网络请求时getCoursesList返回空数组的问题
优惠活动
大模型
产品
解决方案
定价
更多

Swift开发:使用DispatchQueue实现网络请求时getCoursesList返回空数组的问题

问题根因

你遇到的返回空数组问题,本质是同步返回逻辑和异步网络请求的执行时序错位,和DispatchQueue的使用没有直接关联:

  • URLSession发起的网络请求是完全异步执行的:调用request方法后,代码不会阻塞等待网络响应,会立刻往下执行,走到getCoursesList最后的return coursesList语句
  • 此时初始化的空数组coursesList还没有被网络回调的赋值逻辑接触到,返回的自然是空数组
  • 你能在回调里打印到正确数据,是因为网络响应触发回调的时机(通常是几百毫秒到几秒之后),远晚于方法return执行的时机,两个逻辑的执行顺序完全倒置
  • 你在NetworkRequest里用DispatchQueue.main.async把回调切到主线程的操作本身没有问题,这是为了保证回调里可以直接更新UI,但这个操作解决不了时序错位的核心问题

修复方案

网络请求天生是异步流程,绝对不能用同步返回值的方式获取结果,必须改成和NetworkRequest一致的闭包回调形式传递结果:

1. 改造getCoursesList方法

把方法的返回值改成完成回调闭包,等网络请求完成、数据解析结束后,再通过闭包把结果抛出去:

func getCoursesList(completion: @escaping (Result<[Course], Error>) -> Void) {
    let networkRequest = NetworkRequest()
    // 替换强制解包的token逻辑,避免token不存在时崩溃
    guard let token = UserDefaults.standard.string(forKey: "Token") else {
        completion(.failure(NetworkError.tokenNotFound))
        return
    }
    
    networkRequest.request(
        endpoint: "/webservice/rest/server.php",
        parameters: [
            "wstoken": token,
            "wsfunction": "core_course_get_courses",
            "moodlewsrestformat": "json"
        ]
    ) { result in
        switch result {
        case .success(let data):
            do {
                // 成功拿到数据后优先解析为课程数组
                let courses = try JSONDecoder().decode([Course].self, from: data)
                completion(.success(courses))
            } catch {
                // 解析失败时,尝试识别接口返回的业务错误结构
                if let serverError = try? JSONDecoder().decode(ErrorResponse.self, from: data) {
                    print("接口业务错误:\(serverError)")
                    completion(.failure(NetworkError.serverBusinessError(serverError.message)))
                } else {
                    print("数据解析失败:\(error.localizedDescription)")
                    completion(.failure(error))
                }
            }
        case .failure(let error):
            print("网络请求失败:\(error.localizedDescription)")
            completion(.failure(error))
        }
    }
}

2. 正确调用改造后的方法

因为是异步流程,调用时不能用同步赋值的方式接收结果,必须在闭包内处理返回的课程列表:

// 调用示例
getCoursesList { result in
    switch result {
    case .success(let courses):
        // 这里拿到的就是解析完成的完整课程数组,可以直接用来刷新界面、赋值给页面变量
        print("加载完成的课程列表:\(courses)")
    case .failure(let error):
        // 统一处理加载失败的提示、错误上报
        print("加载课程列表失败:\(error.localizedDescription)")
    }
}

附带的小优化建议

  • 原来的NetworkRequest实现里,把响应状态校验、错误判断、数据处理的逻辑全部放在DispatchQueue.main.async块里,虽然不影响功能,但这些校验逻辑完全可以在URLSession的后台回调线程里执行,不需要强制切到主线程,减少不必要的线程切换开销
  • 现在的POST请求没有设置Content-Type头,Moodle接口可能无法正确识别你放在httpBody里的参数,可以补充设置request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type"),避免接口返回参数错误的问题

内容的提问来源于stack exchange,提问作者Lucas Scherer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 01:06:08