如何使用 SwiftUI 在模态视图中创建 NSManagedObject?
Posted
技术标签:
【中文标题】如何使用 SwiftUI 在模态视图中创建 NSManagedObject?【英文标题】:How to create a NSManagedObject in a modal view using SwiftUI? 【发布时间】:2020-09-21 20:42:12 【问题描述】:如何使用 SwiftUI 在模态视图中创建新的 ManagedObject (MO)?
遇到一个奇怪的错误,Xcode 消耗 GB 内存并通过交换文件填满 Mac 上的硬盘。
当在.sheet
修饰符中创建模态视图时,似乎会创建某种无限循环,该循环会用注入该模态视图的 ManagedObject 的副本填充内存。
此示例项目至少说明了部分问题。如果你运行它,你会看到 .sheet 修饰符中调用的方法一遍又一遍地触发。一种理论是,下方显示 ManagedObjects 列表的屏幕会导致两个屏幕之间出现某种循环。
https://github.com/sphericalwave/ChildContextTest
希望在模态屏幕中使用 childContext,因此如果模态视图在未保存 childContext 的情况下被关闭,则任何未保存的更改都将被丢弃。但首先需要清除这个障碍,跨上下文共享 ManagedObject 存在一些挑战。
import CoreData
import SwiftUI
struct CrtFdsUI: View
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: CrtFd.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CrtFd.scale, ascending: true)])
var crtFds: FetchedResults<CrtFd>
@State var showModal = false
@ObservedObject var absFd: AbsFd
func crtFdModal() -> CrtFdUI
print("func crtFdModal() -> CrtFdUI")
let cF = CrtFd(scale: 1.0, absFd: absFd, moc: moc)
return CrtFdUI(crtFd: cF)
var body: some View
NavigationView
VStack
List
ForEach(self.crtFds, id: \.objectID)
CrtFdCell(crtFd: $0)
.navigationBarTitle("CrtFdsUI")
.navigationBarItems(trailing: PlusBtn(showModal: $showModal))
.sheet(isPresented: $showModal) self.crtFdModal() //FIXME: Called in endless loop?
这是 managedObjects 的列表。
import CoreData
import SwiftUI
struct CrtFdsUI: View
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: CrtFd.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CrtFd.scale, ascending: true)])
var crtFds: FetchedResults<CrtFd>
@State var showModal = false
@ObservedObject var absFd: AbsFd
func crtFdModal() -> CrtFdUI
print("func crtFdModal() -> CrtFdUI")
let cF = CrtFd(scale: 1.0, absFd: absFd, moc: moc)
return CrtFdUI(crtFd: cF)
var body: some View
NavigationView
VStack
List
ForEach(self.crtFds, id: \.objectID)
CrtFdCell(crtFd: $0)
.navigationBarTitle("CrtFdsUI")
.navigationBarItems(trailing: PlusBtn(showModal: $showModal))
.sheet(isPresented: $showModal) self.crtFdModal()
【问题讨论】:
【参考方案1】:现在,每次重新计算视图层次结构时,代码都会创建一个 CrtFd
的新实例。这可能不是一个好主意,因为由于您无法直接控制的原因,可能会意外重新计算层次结构,因此即使没有无限循环的创建,您最终可能仍会得到比您想要的更多的新托管对象。
我下载了你的项目,但我不确定这两个 Core Data 实体代表什么,但我确实注意到,当 CrtFdsUI
第一次出现时,它的 absFd
已经具有 crtFd
属性的值,并且crtFd
属性是一对一而不是一对多。这意味着,当您在此代码中创建一个新实例时,您会将一个 CrtFd
替换为另一个完全相同的实例。
我猜你并不想用相同的副本替换一个实例,所以避免这个问题的一种方法是更改你的 crtFdModal()
以使用已经存在的实例,并且仅在还没有副本的情况下创建一个新副本:
func crtFdModal() -> CrtFdUI
print("func crtFdModal() -> CrtFdUI \(showModal)")
let cF = absFd.crtFd ?? CrtFd(scale: 1.0, absFd: absFd, moc: moc)
return CrtFdUI(crtFd: cF)
这避免了您描述的问题。很难判断它是否正是您所需要的,但由于您的代码创建了明显不必要的重复项,因此很可能确实如此。
【讨论】:
对不起,令人困惑。这是一个示例项目,因为我真的不知道问题是什么。将 CrtFd 视为您吃的食物(100 克萝卜)的一个实例。 AbsFd 是食物的概念(1 x 100 克萝卜有一堆相关的大量营养素,图片等)。因此,当您创建 CrtFd 时,您的想法是记录您吃过的食物的实例。 所以 AbsFd 可以知道许多 CrtFd(我没有在 Core Data 模型中正确建立这种关系,但我只是在 GitHub 上更新了它)。 上面的解决方案不是我想要的,因为当您按下 + 按钮时,您正在记录Turnip
的新实例。所以我确实想创建一个新的 CrtFd 并将其与 AbstractFood 相关联。我只是对要使用的 absFd 进行硬编码,因为这样我可以简化示例项目。它的连接方式没有多大意义,但它展示了 ViewHierarchy 重新计算并导致循环的挑战。
我明白了。如果您的问题包含这些关键细节,将会有所帮助。以上是关于如何使用 SwiftUI 在模态视图中创建 NSManagedObject?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中关闭 ResearchKit 模态视图?