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

Swift字典删除元素后无法新增条目,求助排查问题原因

问题分析与修复方案

你遇到的问题主要来自两个核心错误:Card结构体的Equatable协议实现完全不符合预期,以及字典元素操作的逻辑漏洞,我来逐个拆解并给出修复方案:

1. 致命问题:Card的Equatable实现完全错误

你当前的==方法判断的是两张卡牌是否能组成游戏中的"Set"组合,而不是判断两张卡牌是否是完全相同的实例。这会引发连锁问题:

  • game.cards.index(of: randomCard)会返回错误的索引(比如找到第一张满足Set规则的卡牌,而非你选中的随机卡)
  • 调用game.cards.remove(at: ...)时,删除的根本不是你选中的那张卡,导致game.cards元素没有正确减少,后续添加逻辑完全混乱
  • 甚至可能因为找不到正确索引,强制解包!导致程序崩溃(你的代码没崩溃只是巧合)

正确的Card Equatable实现应该判断卡牌的核心属性完全一致(如果状态属性isSelected/isSet不需要参与相等判断,可以去掉):

struct Card: Equatable {
    static func == (lhs: Card, rhs: Card) -> Bool {
        // 只判断卡牌本身的属性,状态属性不参与相等判断
        return lhs.number == rhs.number &&
               lhs.symbol == rhs.symbol &&
               lhs.shading == rhs.shading &&
               lhs.color == rhs.color
    }
    
    var number = 0
    var symbol = 0
    var shading = 0
    var color = 0
    var isSelected = false
    var isSet = false
}

2. 字典操作的逻辑漏洞

(1)删除元素时的随机key无效

你用game.cardField.count.random生成随机key,但字典的key并不是连续的!比如删除3个元素后,字典count是21,但key可能是0-23中缺失3个值,此时生成的0-20之间的key可能根本不存在,导致removeValue(forKey:)什么都不做。

正确的删除方式应该先获取字典所有存在的key,再随机选择:

for _ in 1...3 {
    guard let randomKey = game.cardField.keys.randomElement() else { break }
    game.cardField.removeValue(forKey: randomKey)
}

(2)添加元素时的索引选择优化

你原来通过index(of:)找索引的方式,因为Equatable的错误已经失效,改成直接生成随机索引更高效可靠:

let randomIndex = game.cards.count.random
let randomCard = game.cards[randomIndex]
game.cards.remove(at: randomIndex)

3. 值类型的影响(次要但需了解)

gameSet是结构体(值类型),当你赋值或传递时会触发深拷贝,但你直接操作的是同一个var game实例,所以修改属性是有效的,这部分不是你当前问题的核心。

完整修复后的代码

顺便修正了Swift命名规范(结构体首字母大写):

// 正确实现Equatable的Card结构体
struct Card: Equatable {
    static func == (lhs: Card, rhs: Card) -> Bool {
        return lhs.number == rhs.number &&
               lhs.symbol == rhs.symbol &&
               lhs.shading == rhs.shading &&
               lhs.color == rhs.color
    }
    
    var number = 0
    var symbol = 0
    var shading = 0
    var color = 0
    var isSelected = false
    var isSet = false
}

// 符合命名规范的游戏模型
struct GameSet {
    var cards = [Card]()
    var cardField = [Int: Card]()
    
    init() {
        for num in 1...3 {
            for sym in 1...3 {
                for sha in 1...3 {
                    for col in 1...3 {
                        cards.append(Card(number: num, symbol: sym, shading: sha, color: col, isSelected: false, isSet: false))
                    }
                }
            }
        }
    }
}

// 随机数扩展
extension Int {
    var random: Int {
        return Int(arc4random_uniform(UInt32(self)))
    }
}

// 主逻辑
var game = GameSet()

// 填充24个随机卡牌到字段
var cardCount = 24
repeat {
    guard game.cards.count > 0 else { break }
    let randomIndex = game.cards.count.random
    let randomCard = game.cards[randomIndex]
    game.cardField[game.cardField.count] = randomCard
    game.cards.remove(at: randomIndex)
    cardCount -= 1
} while cardCount != 0
print(game.cardField.count) // 输出24

// 删除3个随机卡牌
for _ in 1...3 {
    guard let randomKey = game.cardField.keys.randomElement() else { break }
    game.cardField.removeValue(forKey: randomKey)
}
print(game.cardField.count) // 输出21

// 添加3个新卡牌
var cardCount2 = 3
repeat {
    guard game.cards.count > 0 else { break }
    let randomIndex = game.cards.count.random
    let randomCard = game.cards[randomIndex]
    game.cardField[game.cardField.count] = randomCard
    game.cards.remove(at: randomIndex)
    cardCount2 -= 1
} while cardCount2 != 0
print(game.cardField.count) // 输出24

总结

你的核心问题是Card的Equatable实现错误,导致后续的索引查找、元素删除全部失效,进而让添加操作看起来毫无效果。修复Equatable实现后,配合正确的字典元素操作逻辑,问题就能彻底解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:43:48