@StateObject 用于没有上下文的 NSManagedObject 不发布更改
Posted
技术标签:
【中文标题】@StateObject 用于没有上下文的 NSManagedObject 不发布更改【英文标题】:@StateObject for a NSManagedObject without context not publishing changes 【发布时间】:2020-11-03 10:48:12 【问题描述】:与许多应用程序一样,我有一个项目列表(由 Core Data 获取请求填充)、一个用于创建新项目的工作表以及一个用于在点击列表中的一行时编辑项目的工作表。我正在尝试统一两个表单以创建和编辑更新,并将取消/保存逻辑放在表单的超级视图中。
所以我有这样的东西:
ListView:由 Core Data 获取请求填充的行列表 AddView:带有 FormView 嵌入 + 取消和保存按钮的 NavigationView EditView:带有 FormView 嵌入 + 取消和保存按钮的 NavigationView FormView:用于更新项目名称的 TextField在 AddView 的 init() 中,我创建了一个没有任何上下文的新 NSManagedObject(我这样做是因为我不希望在 AddView 中创建新项目时更新我的 ListView,但仅在我保存时此项目-> 替代方法可以是使用子上下文,或根据返回对象的 isInserted 或 objectID.isTemporaryID 过滤获取请求结果)。 AddView 包含一个带有 FormView 嵌入的 NavigationView、一个取消按钮和一个保存按钮。此保存按钮根据托管对象上的计算属性被禁用(对象的名称不能为 nil)。
在 EditView 中,我传递了从 ListView 中点击的项目。此项目是附加到应用程序主 viewContext 的现有 NSManagedObject(来自 ListView 的获取请求)。 EditView 包含一个带有 FormView 嵌入的 NavigationView、一个取消按钮和一个保存按钮(与 AddView 完全相同)。基于相同的计算属性,此保存按钮也被禁用。
我的问题是,当我从 FormView 中的 TextField 更新项目名称时,启用/禁用保存按钮的条件不适用于 AddView(当我更改项目名称时,此 AddView 实际上没有刷新来自 FormView)但适用于 EditView(当我从 FormView 更改项目名称时,此 EditView 会刷新)。如果我在 AddView 的 init() 中将上下文附加到新的 NSManagedObject,则条件就像在 EditView 中一样。
所以 SwiftUI 似乎没有观察到没有任何上下文的 NSManagedObject?我错过了什么还是一个错误?
【问题讨论】:
NSManagedObject 在没有上下文的情况下无效,所以我在这里看不到任何错误。 好的。实现我想要做的最优雅的方式是什么?我最初开始使用临时结构来充当 NSMmagedObject 的代理并基于该结构创建 NSManagedObject 但考虑只使用 MO 而不是进行此转换。 【参考方案1】:如果托管对象的更改通知能力取决于上下文的存在,我不会感到惊讶(但尚未验证)。我想不出你想要创建一个没有上下文的托管对象的情况。
您应该使用子上下文。上下文在 Core Data 中为您做了很多工作(管理关系,可能更改通知,验证等),并提供了一种简单的方法来取消/保存更改 - 只需保存子上下文,数据流回主上下文,或丢弃要放弃的上下文。
【讨论】:
感谢您的回答。我要编辑的现有对象呢?我应该从 EditView 的 init 中使用父视图 (ListView) 提供的 objectID 获取对象吗?此外,在 EditView init() 上无法访问环境上下文。因此,要么传递对上下文的引用,要么等待保存操作将父上下文关联到子上下文(这样我也可以保存父上下文)。 获取更改通知的解决方法是将此覆盖添加到 NSManagedObject 子类覆盖 public func willChangeValue(forKey key: String) super.willChangeValue(forKey: key) self.objectWillChange.send() 对于要编辑的现有对象,您必须在编辑上下文中“实现”该对象,因此您需要创建新上下文,然后使用existingObjectWithID
获取可编辑版本编辑。在哪里创建上下文以及何时保存它取决于您的应用程序流程。
@jrtutron 不会创建新的上下文并将对象放入其中会破坏单一事实来源模式? NSManagedObject 实例将在这个新上下文中,以及在驱动应用程序中其他视图的 viewContext 中。
如果您想要可丢弃的编辑,您就不能拥有单一的事实来源。每个上下文都有自己的相关托管对象的实例,在编辑上下文中所做的更改不会影响主上下文,直到编辑上下文被保存。【参考方案2】:
获取更改通知的解决方法是将此覆盖添加到 NSManagedObject 子类:
override public func willChangeValue(forKey key: String)
super.willChangeValue(forKey: key)
self.objectWillChange.send()
可以对 NSManagedObject 进行子类化以添加此覆盖(更多信息 here 和 here)
如果我们不想触发每个键的更改,我们可以更具体地确定更新值。这也适用于关系(上述解决方案并非如此)。
func setName(_ name: String)
objectWillChange.send()
self.name = name
在这种情况下,即使观察到的对象没有上下文,我的 AddView 也会更新(更改通知可能仅在对象存在上下文时触发)。保存按钮根据我的 NSManagedObject 子类中的以下计算属性被禁用/启用。
var canBeSaved: Bool
if self.name.isEmpty
return false
else
return true
【讨论】:
我强烈建议您不要使用没有上下文的托管对象。以上是关于@StateObject 用于没有上下文的 NSManagedObject 不发布更改的主要内容,如果未能解决你的问题,请参考以下文章
福利来了:纳雷科技发布大陆雷达最新测试工具NSM_CAR Tools,免费试用
SwiftUI onChange(of: ...) 更新 StateObject
如何防止 SwiftUI 像使用 @StateObject 一样重新初始化我的包装属性?