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

Swift 4 Xcode 9:API请求无法及时返回数据填充TableView

嘿,我太懂你这种异步请求和TableView加载不同步的头疼了——网络请求是后台异步跑的,而viewDidLoad是主线程同步执行的,所以TableView总是先尝试渲染单元格,这时候数据还没从服务器拿回来,自然就空了。我帮你拆解下问题,再给你靠谱的解决方案:

问题根源

你之前的完成处理程序没生效,大概率是两个原因:要么没在完成回调里触发TableView刷新,要么没切回主线程更新UI(网络请求的回调默认在后台线程,直接操作UI会出问题)。至于信号量,千万别在主线程用wait(),这会直接卡死UI,完全违背iOS的异步设计原则。

正确的完成处理程序用法

先把API请求封装成带完成闭包的函数,确保在数据解析完成后切回主线程触发回调:

// 先定义你的数据模型,比如叫DataItem
struct DataItem: Codable {
    let id: Int
    let title: String
    // 其他字段根据你的API返回定义
}

// 封装API请求函数,带完成处理程序
func fetchTableData(completion: @escaping ([DataItem]) -> Void) {
    guard let requestUrl = URL(string: "你的API接口地址") else {
        completion([])
        return
    }
    
    URLSession.shared.dataTask(with: requestUrl) { data, _, error in
        // 处理网络错误
        guard let responseData = data, error == nil else {
            print("网络请求错误:\(error?.localizedDescription ?? "未知错误")")
            completion([])
            return
        }
        
        // 解析JSON数据
        do {
            let parsedData = try JSONDecoder().decode([DataItem].self, from: responseData)
            // 必须切回主线程,因为要更新UI相关的操作
            DispatchQueue.main.async {
                completion(parsedData)
            }
        } catch {
            print("数据解析错误:\(error)")
            completion([])
        }
    }.resume() // 别忘了启动请求!
}

然后在viewDidLoad里调用这个函数,拿到数据后刷新TableView:

class YourTableViewController: UITableViewController {
    // 数据源数组
    private var dataSource: [DataItem] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 先注册单元格(如果没在Storyboard里注册的话)
        tableView.register(YourCustomCell.self, forCellReuseIdentifier: "CellIdentifier")
        
        // 发起数据请求
        fetchTableData { [weak self] fetchedData in
            guard let self = self else { return }
            // 更新数据源
            self.dataSource = fetchedData
            // 刷新TableView
            self.tableView.reloadData()
        }
    }

    // 实现TableView的数据源方法
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath) as! YourCustomCell
        let item = dataSource[indexPath.row]
        cell.titleLabel.text = item.title
        // 配置其他单元格内容
        return cell
    }
}

更简洁的Swift 5.5+方案:Async/Await

如果你的项目用的是Swift 5.5及以上版本,用Async/Await会让代码更清爽,不用写嵌套闭包:

// 用Async/Await重构请求函数
func fetchTableData() async throws -> [DataItem] {
    guard let requestUrl = URL(string: "你的API接口地址") else {
        throw NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的API地址"])
    }
    
    let (data, _) = try await URLSession.shared.data(from: requestUrl)
    return try JSONDecoder().decode([DataItem].self, from: data)
}

// 在viewDidLoad里调用(用Task包裹异步代码)
override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.register(YourCustomCell.self, forCellReuseIdentifier: "CellIdentifier")
    
    Task {
        do {
            let fetchedData = try await fetchTableData()
            // 切回主线程刷新UI
            DispatchQueue.main.async {
                self.dataSource = fetchedData
                self.tableView.reloadData()
            }
        } catch {
            print("请求失败:\(error.localizedDescription)")
            // 这里可以给用户显示错误提示,比如弹出Alert
        }
    }
}

最后检查这几个关键点

  1. 确认你在拿到数据后调用了tableView.reloadData()
  2. 所有更新UI的操作(包括刷新TableView)都必须在主线程执行
  3. 数据源数组被正确赋值,没有被意外清空或者覆盖

内容的提问来源于stack exchange,提问作者Cameron Reilly

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:15:21