为何ViewModel中修改一个列表会同时改变另一个列表?
问题原因及解决方案
原因分析
你遇到的同步变更问题,核心是对象引用共享导致的:
- 执行
tasks.addAll(data.tasks)时,页面本地的tasks列表只是复制了ViewModel中data.tasks里TaskItem对象的引用,两个列表中的TaskItem是同一个实例,它们内部的Task对象自然也是同一个引用。 - 虽然你的
onDone扩展函数通过copy()创建了新的TaskItem和Task,但如果存在其他直接修改Task属性的操作(比如tasks[index].task.done = !tasks[index].task.done),或者onDone逻辑未被正确执行,就会通过引用关系同步修改ViewModel中data.tasks的done值。 - 另外,如果
DataModel的tasks绑定了数据库(如Room)的可观察数据源,页面修改可能触发自动同步,导致ViewModel数据被更新。
解决方案
要切断引用关联,需要对数据进行深度拷贝,确保本地列表的对象完全独立:
1. 修改数据初始化逻辑,深度拷贝TaskItem
在LaunchedEffect中,对每个TaskItem及其内部的Task进行拷贝,避免引用共享:
LaunchedEffect(key1 = Unit) { tasks.clear() val copiedTasks = data.tasks.map { taskItem -> // 同时拷贝TaskItem和内部的Task,彻底切断引用 taskItem.copy(task = taskItem.task.copy()) } tasks.addAll(copiedTasks) }
2. 坚持不可变更新模式
你的onDone扩展函数已经采用了正确的“创建新对象替换旧对象”的方式,继续保持这种模式,避免直接修改现有对象的属性:
fun List<TaskItem>.onDone(index: Int): TaskItem { val item = this[index] return item.copy(task = item.task.copy(done = !item.task.done)) }
调用时通过替换列表元素完成更新:
onDone = { index -> tasks[index] = tasks.onDone(index) }
3. 控制数据库同步时机(如果涉及)
如果DataModel的tasks绑定了数据库,确保只有在用户明确触发保存操作时,才将本地修改同步到数据库,避免自动同步导致的意外变更。
内容的提问来源于stack exchange,提问作者Mark Delphi
相关产品推荐
相关产品推荐

