【问题标题】:LiveData "pass-by-reference" initial valueLiveData“按引用传递”初始值
【发布时间】:2020-12-26 22:06:22
【问题描述】:

我有一个看起来像这样的ViewModel 类:

class EditUserViewModel(
    private val initUser: User,
) : ViewModel() {

    private val _user = MutableLiveData(initUser)
    val user: LiveData<User>
        get() = _user

    fun hasUserChanged() = initUser != _user.value

}

User可以通过UI更新User数据类实例的一些属性。
要检查从片段导航时是否有任何更改,我使用hasUserChanged 方法。
问题是这总是错误的。我检查了一下,似乎每次更改_user MutableLiveDatainitialUser 都会发生变化。
这是为什么? MutableLiveData的初始值是通过引用传递的吗?我一直认为 Kotlin 是一种“按值传递”类型的语言。

更新: 在将 initUser 复制到 MutableLiveData 之前,问题似乎消失了。

private val _user = MutableLiveData(initUser.copy())

但我仍然不明白为什么我必须这样做。

【问题讨论】:

    标签: android kotlin pass-by-reference android-livedata pass-by-value


    【解决方案1】:

    Kotlin 就像 java,它们是按值传递的。如果您在User 类中实现equals 函数,或者将其设为data class(它隐式实现了equals 函数),它可以确保用户对象的内容被!= 运算符检查.

    更新

    如果你直接改变LiveData的值,比如这样:

    _user.value.name = "some name"
    

    这意味着您正在更改initUsername 属性,因为_user.value 正是指initUser 所做的对象。因此,!= 运算符总是返回 false,因为我们有一个对象有两个对它的引用。

    现在,当你这样做时:

    private val _user = MutableLiveData(initUser.copy())
    

    您正在创建initUser(我们称之为X)的深层副本,它是内存中的一个新对象,具有与initUser 相同的属性值。

    因此,通过更改其属性,例如:_user.value.name = "some name",实际上,您是在X 上进行此更改,而不是initUser。这导致保留initUser 中的初始值,这意味着不要更改它们,并解决问题。

    【讨论】:

    • User 类确实是data class,所以hasUserChanged 方法可以正确比较它们。但问题是这两个比较值是相同的 - 每次更新 _recipe 值时都会更新 initUser
    • 好的,所以如果它是一个数据类,它不介意!=检查两个具有相同内容的对象的相等性或检查一个对象本身。我想问题是别的。请更改 hasUserChanged 如下,然后检查 logcat 输出:fun hasUserChanged(): Boolean { println("initUser: ${initUser.toString()}") println("_user.value: ${_user.value.toString()}") return initUser != _user.value }
    • 我完全按照你说的做了,这两个对象是一样的,每个值都是一样的。我还使用data binding 在 xml 文件中使用“viewModel.recipe”更新 UI。但我不知道这是否是我的问题的原因。
    猜你喜欢
    • 2011-09-04
    • 2013-04-12
    • 2012-01-16
    • 2012-10-14
    • 2014-08-23
    • 2015-05-23
    • 1970-01-01
    • 2011-05-08
    相关资源
    最近更新 更多