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

Swift 4 数组越界致命错误求助:洗牌问题数组后程序崩溃

解决Swift 4中数组打乱导致的"Index out of range"崩溃问题

我帮你分析下代码里的问题,以及对应的修复方案:

问题根源

你遇到的崩溃主要有两个核心原因:

  1. randomNumber是类属性,只初始化一次:这个变量在类初始化时就生成了固定的随机值,比如第一次是3,但后续调用newQuestion时,questions数组会被清空(因为你在循环里执行了questions.remove(at: randomNumber)),再用这个固定的索引去访问空数组,必然会越界。
  2. 错误地修改了原始questions数组:每次调用newQuestion,你都会把questions里的元素全部移到shuffled数组,导致原数组被掏空,下次调用时数组为空,自然无法通过索引访问元素。

修复后的完整代码

我调整了你的代码,保留原始数据的同时实现正确的打乱逻辑,并且修复了索引越界问题:

class ThirdViewController: UIViewController {
    // 保留原始的问题和答案数组,不要直接修改它们
    let originalQuestions = ["A", "B", "C", "D", "E"]
    let originalAnswers = [["1","2","3"],["2","1","3"],["3","2","1"],["1","3","2"],["2","3","1"]]
    
    // 当前使用的问题数组(用于打乱,不影响原始数据)
    var questions: [String] = []
    var answers: [[String]] = []
    
    var rightAnswerPlacement: UInt32 = 0
    
    // Label Question
    @IBOutlet weak var questionLabel: UILabel! // 建议给控件起有意义的名字
    
    @IBAction func buttonTapped(_ sender: UIButton) { // 同样,方法名更清晰
        if sender.tag == Int(rightAnswerPlacement) {
            print("RIGHT")
        } else {
            print("WRONG")
        }
        newQuestion()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated) // 别忘了调用父类方法
        self.navigationController?.isNavigationBarHidden = true
        // 初始化时复制原始数据并打乱
        resetAndShuffleData()
        newQuestion()
    }
    
    // 重置并打乱问题和答案数组(保证两者配对)
    func resetAndShuffleData() {
        // 先复制原始数据
        questions = originalQuestions
        answers = originalAnswers
        
        // 同时打乱问题和答案数组,保持对应关系
        let combined = zip(questions, answers).shuffled()
        questions = combined.map { $0.0 }
        answers = combined.map { $0.1 }
    }
    
    func newQuestion() {
        guard !questions.isEmpty else {
            // 如果问题都用完了,重置并重新打乱
            resetAndShuffleData()
        }
        
        // 每次生成新的随机索引,基于当前questions的数量
        let randomNumber = Int(arc4random_uniform(UInt32(questions.count)))
        questionLabel.text = questions[randomNumber]
        
        rightAnswerPlacement = arc4random_uniform(3) + 1
        
        // 更新按钮标题
        var x = 1
        for i in 1...3 {
            if let button = view.viewWithTag(i) as? UIButton {
                if i == Int(rightAnswerPlacement) {
                    button.setTitle(answers[randomNumber][0], for: .normal)
                } else {
                    button.setTitle(answers[randomNumber][x], for: .normal)
                    x = 2
                }
            }
        }
        
        // 如果需要每次用过后移除当前问题,避免重复
        // questions.remove(at: randomNumber)
        // answers.remove(at: randomNumber)
    }
}

关键修改点说明

  • 分离原始数据和工作数据:把originalQuestions和originalAnswers设为常量,作为数据源,工作时用复制后的数组,避免原始数据被破坏。
  • 同时打乱配对数组:用zip把问题和答案配对后再打乱,保证每个问题对应的答案不会错位。
  • 每次生成新的随机索引:在newQuestion内部生成随机数,而不是用类属性的固定值,确保索引始终在当前数组的有效范围内。
  • 添加空数组判断:防止所有问题都被移除后出现空数组访问的情况,自动重置数据。
  • 优化命名:给控件和方法起更清晰的名字,提升代码可读性。

另外,如果你使用的是Swift 4.0(还未自带shuffled()方法),可以自己实现一个简单的打乱扩展:

extension Collection {
    func shuffled() -> [Element] {
        var array = Array(self)
        array.shuffle()
        return array
    }
}

extension MutableCollection where Indices.Iterator.Element == Index {
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }
        
        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:17:03