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

Swift 5中UITableView返回数组计数时触发数组越界错误

问题分析与修复方案

我来帮你梳理下当前代码里的核心问题,并且给出完整的修复步骤:

核心问题拆解

  1. 数组越界错误:你定义的sample是[News]类型(News对象的数组),但实际上接口返回的是单个News对象(内部包含articles数组)。你解码后把这个单个对象塞进了数组,但是在tableView的数据源方法里直接访问sample[0]——如果网络请求还没完成,sample还是空数组,这时候必然触发数组越界。而且这种数组存储的方式完全没必要,反而增加了复杂度。

  2. tableView行数显示异常:你提到返回sample.count时只显示1个单元格,这是因为sample数组里只有1个元素(你塞进去的那个News对象),所以sample.count等于1,自然只会显示一行。而你真正需要的是articles数组的元素数量。


分步修复

1. 调整数据存储结构

把原来的数组类型存储改成单个可选对象,因为接口返回的是单个News实例:

// 替换原来的 var sample = [News]()
var news: News?

2. 修正JSON解码逻辑

在getJson()方法里,直接解码为单个News对象,而不是放到数组中,同时优化遍历和打印逻辑:

func getJson() {
    // 注意:把url里的&改成&,HTML转义字符在URL里无法被正确识别
    let urlString = "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=e07d26ea273d41e2af174b026aea27b5"
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else { 
            print("请求无数据返回")
            return 
        }
        do {
            // 直接解码为单个News对象
            self.news = try JSONDecoder().decode(News.self, from: data)
            // 安全打印articles数量
            print("文章总数:\(self.news?.articles.count ?? 0)")
            // 遍历打印标题,避免强制解包
            self.news?.articles.forEach { 
                print($0.title ?? "无标题")
            }
            DispatchQueue.main.async {
                self.table.reloadData()
            }
        } catch let jsonErr {
            print("JSON解码错误: ", jsonErr)
        }
    }.resume()
}

3. 修复tableView数据源方法

修改数据源方法,安全访问数据,避免空值和越界:

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // 如果news为空,返回0;否则返回articles的数量
        return news?.articles.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
        // 安全获取当前行的文章数据,避免越界和空值
        guard let article = news?.articles[indexPath.row] else {
            cell.name.text = "无标题"
            cell.link.text = "无描述"
            cell.imageUrl.text = "无图片链接"
            return cell
        }
        // 用空合并运算符处理可选值,防止崩溃
        cell.name.text = article.title ?? "无标题"
        cell.link.text = article.description ?? "无描述"
        cell.imageUrl.text = article.urlToImage ?? "无图片链接"
        return cell
    }
}

4. 额外的安全优化

  • 把URL中的&替换为&:因为&是HTML的转义字符,直接放在URL里会导致请求参数解析错误,可能返回非预期的结果。
  • 避免所有的强制解包(!):改用可选绑定(guard let)或空合并运算符(??)处理可选值,防止因为接口返回null或者数据未加载完成导致崩溃。

完整修正后的代码

import UIKit
struct News: Decodable {
    let articles: [Article]
    struct Article: Decodable {
        let title: String?
        let urlToImage: String?
        let description: String?
    }
}
class ViewController: UIViewController {
    @IBOutlet weak var table: UITableView!
    // 调整为单个可选对象
    var news: News?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        getJson()
        table.delegate = self
        table.dataSource = self
    }
    
    func getJson() {
        // 修正URL中的转义字符
        let urlString = "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=e07d26ea273d41e2af174b026aea27b5"
        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { 
                print("请求无数据返回")
                return 
            }
            do {
                self.news = try JSONDecoder().decode(News.self, from: data)
                print("文章总数:\(self.news?.articles.count ?? 0)")
                self.news?.articles.forEach { 
                    print($0.title ?? "无标题")
                }
                DispatchQueue.main.async {
                    self.table.reloadData()
                }
            } catch let jsonErr {
                print("JSON解码错误: ", jsonErr)
            }
        }.resume()
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return news?.articles.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
        guard let article = news?.articles[indexPath.row] else {
            cell.name.text = "无标题"
            cell.link.text = "无描述"
            cell.imageUrl.text = "无图片链接"
            return cell
        }
        cell.name.text = article.title ?? "无标题"
        cell.link.text = article.description ?? "无描述"
        cell.imageUrl.text = article.urlToImage ?? "无图片链接"
        return cell
    }
}

内容的提问来源于stack exchange,提问作者Stephenson Ang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:19:46