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
相关产品推荐
相关产品推荐

