Swift中修改ScoreModel数组内HoleScore属性时的批量变更异常
嘿,这个问题我之前踩过坑!核心原因是Swift里的类是引用类型——你肯定是在创建ScoreModel实例的时候,不小心让多个实例共享了同一个HoleScore对象(或者同一个holeScores数组里的元素),导致改一个属性,所有指向这个对象的引用都会跟着变。
比如举个反例,如果你是这么创建数组的:
// 错误示范:复用同一个HoleScore实例 let sharedHoleScore = HoleScore() let holeScores = [sharedHoleScore] let model1 = ScoreModel() model1.holeScores = holeScores let model2 = ScoreModel() model2.holeScores = holeScores scoreList = [model1, model2]
这时候model1和model2的holeScores[0]指向的是同一个sharedHoleScore对象,你改其中一个的holeScore,另一个自然也会同步变化,因为它们本质上操作的是内存里的同一个对象。
解决方案:确保每个实例都是独立的
要解决这个问题,关键是让每个ScoreModel的holeScores数组及其内部的HoleScore实例都是全新创建的,不要复用同一个对象引用。
1. 手动创建独立实例
创建每个ScoreModel的时候,单独初始化它的holeScores数组和里面的HoleScore对象:
// 创建第一个独立的ScoreModel let model1 = ScoreModel() model1.playerId = 1 model1.holeScores = [HoleScore()] // 这里每次都生成新的HoleScore实例 // 创建第二个独立的ScoreModel let model2 = ScoreModel() model2.playerId = 2 model2.holeScores = [HoleScore()] scoreList = [model1, model2]
现在再执行scoreList[0].holeScores![0].holeScore = 3,就只有model1的属性会被修改,model2的保持原样。
2. 用工厂方法批量创建(更高效)
如果需要创建多个实例,写个工厂方法来统一生成独立的模型,避免重复代码:
extension ScoreModel { static func makePlayerScore(playerId: Int, holeCount: Int = 18) -> ScoreModel { let model = ScoreModel() model.playerId = playerId // 为每个洞生成全新的HoleScore实例 model.holeScores = (1...holeCount).map { _ in HoleScore() } return model } } // 批量生成独立的ScoreModel实例 scoreList = [ ScoreModel.makePlayerScore(playerId: 1), ScoreModel.makePlayerScore(playerId: 2), ScoreModel.makePlayerScore(playerId: 3) ]
3. 优化模型(可选但推荐)
建议尽量避免使用可选类型,减少强制解包!带来的崩溃风险,同时让模型更健壮:
class ScoreModel { var playerId: Int var holeScores: [HoleScore] // 初始化方法强制传入必要参数 init(playerId: Int, holeScores: [HoleScore]) { self.playerId = playerId self.holeScores = holeScores } } class HoleScore { var holeScore: Int = 0 // 给默认值,避免可选 init(holeScore: Int = 0) { self.holeScore = holeScore } }
这样创建实例的时候更清晰,也不会出现nil的情况:
let model1 = ScoreModel(playerId: 1, holeScores: [HoleScore()]) scoreList.append(model1)
总结
本质就是要区分值类型和引用类型的差异:Swift的类是引用类型,赋值的时候传递的是内存地址,而不是对象本身。只要保证每个ScoreModel及其内部的HoleScore都是独立创建的实例,就不会出现“牵一发而动全身”的问题了。
内容的提问来源于stack exchange,提问作者Mujahid Latif

