SwiftUI持有对导致崩溃的已删除核心数据对象的引用 [英] SwiftUI holding reference to deleted core data object causing crash

查看:75
本文介绍了SwiftUI持有对导致崩溃的已删除核心数据对象的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现无法在SwiftUI中使用核心数据,因为当我将核心数据传递给观察到的对象变量的视图时,即使视图消失了,导航链接视图也将保留对对象的引用,因此我从应用程序崩溃的上下文中删除了该对象,没有错误消息.

Im finding it impossible to use core data with SwiftUI, because as I pass a core data to a view observed object variable, the navigation link view will hold a reference to the object even after the view has disappeared, so as soon as I delete the object from context the app crashes, with no error messages.

我已经通过将核心数据对象变量包装到一个视图模型中作为可选内容来确认了这一点,然后在上下文删除操作和应用程序正常运行之后立即将对象设置为nil,但这不是解决方案,因为我需要核心数据对象绑定到迅速的ui视图并成为真相的来源.这应该如何工作?我似乎无法使用SwiftUI使任何事情变得遥不可及.

I have confirmed this by wrapping the core data object variable into a view model as an optional, then set the object to nil right after the context delete action and the app works fine, but this is not a solution because I need the core data object to bind to the swift ui views and be the source of truth. How is this suppose to work? I seriously cannot make anything remotely complex with SwiftUI it seems.

我尝试将传入的核心数据对象分配给可选的@State,但这不起作用.我不能使用@Binding,因为它是一个获取的对象.而且我不能使用变量,因为swiftui控件需要绑定.仅使用@ObservedObject是有意义的,但这不是可选的,这意味着当分配给它的对象被删除时,应用程序崩溃,因为我无法将其设置为nil.

I have tried assigning the passed in core data object to a optional @State, but this does not work. I cannot use @Binding because it's a fetched object. And I cannot use a variable, as swiftui controls require bindings. It only makes sense to use a @ObservedObject, but this cannot be an optional, which means when the object assigned to it gets deleted, the app crashes, because i cannot set it to nil.

这是核心数据对象,默认情况下是可观察的对象:

Here is the core data object, which is an observable object by default:

class Entry: NSManagedObject, Identifiable {

    @NSManaged public var date: Date
}

这里是将核心数据输入对象传递到另一个视图的视图.

Here is a view that passes a core data entry object to another view.

struct JournalView: View {

    @Environment(\.managedObjectContext) private var context

    @FetchRequest(
        entity: Entry.entity(),
        sortDescriptors: [],
        predicate: nil,
        animation: .default
    ) var entries: FetchedResults<Entry>

    var body: some View {
        NavigationView {
            List {
                ForEach(entries.indices) { index in
                    NavigationLink(destination: EntryView(entry: self.entries[index])) {
                        Text("Entry")
                    }
                }.onDelete { indexSet in
                    for index in indexSet {
                        self.context.delete(self.entries[index])
                    }
                }
            }
        }
    }
}

现在是从访问的核心数据条目对象访问所有属性的视图.顺便说一句,我从任何视图中删除了该条目,顺便说一句,它仍然在这里被引用,并导致应用程序立即崩溃.我认为这也与导航链接在访问所有目标视图之前进行初始化有关.为什么这样做会没有道理.这是一个错误,还是有更好的方法来实现?

Now here is the view that accesses all the attributes from the core data entry object that was passed in. Once, I delete this entry, from any view by the way, it is still referenced here and causes the app to crash immediately. I believe this also has something to do with the Navigation Link initializing all destination view before they are even accessed. Which makes no sense why it would do that. Is this a bug, or is there a better way to achieve this?

我什至尝试删除onDisappear都没有成功.即使我从JournalView中删除,由于NavigationLink仍在引用该对象,它仍然会崩溃.有趣的是,如果删除尚未单击的NavigationLink,它也不会崩溃.

I have even tried doing the delete onDisappear with no success. Even if I do the delete from the JournalView, it will still crash as the NavigationLink is still referencing the object. Interesting it will not crash if deleting a NavigationLink that has not yet been clicked on.

struct EntryView: View {

    @Environment(\.managedObjectContext) private var context
    @Environment(\.presentationMode) private var presentationMode

    @ObservedObject var entry: Entry

    var body: some View {
        Form {

            DatePicker(selection: $entry.date) {
                Text("Date")
            }

            Button(action: {
                self.context.delete(self.entry)
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Delete")
            }
        }
    }
}

更新

崩溃使我第一次进入EntryView,并读取线程1:EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP,subcode = 0x0)..那是唯一抛出的消息.

The crash is taking me to the first use of entry in the EntryView and reads Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).. thats the only message thrown.

我能想到的唯一解决方法是向核心数据对象"isDeleted"添加一个属性并将其设置为true,而不是尝试从上下文中删除.然后,当应用程序退出或启动时,我可以清除并删除所有isDeleted吗?这不是理想的选择,而是希望在这里找出问题所在,因为看来我与MasterDetailApp示例并没有做任何不同的事情,该示例似乎可以正常工作.

The only work around I can think of is to add a property to the core data object "isDeleted" and set it to true instead of trying to delete from context. Then when the app is quit, or on launch, I can clean and delete all entries that isDeleted? Not ideal, and would prefer to figure out what it wrong here, as it appears I'm not doing anything different then the MasterDetailApp sample, which seems to work.

推荐答案

我基本上遇到了同样的问题.看来SwiftUI会立即加载每个视图,因此该视图已使用现有CoreData对象的Properties进行加载.如果在通过@ObservedObject访问某些数据的视图中删除它,它将崩溃.

I basically had the same issue. It seems that SwiftUI loads every view immediately, so the view has been loaded with the Properties of the existing CoreData Object. If you delete it within the View where some data is accessed via @ObservedObject, it will crash.

我的解决方法:

  1. 删除操作"-已推迟,但已通过通知中心结束

    Button(action: {
      //Send Message that the Item  should be deleted
       NotificationCenter.default.post(name: .didSelectDeleteDItem, object: nil)

       //Navigate to a view where the CoreDate Object isn't made available via a property wrapper
        self.presentationMode.wrappedValue.dismiss()
      })
      {Text("Delete Item")}

您需要定义一个Notification.name,例如:

You need to define a Notification.name, like:

extension Notification.Name {

    static var didSelectDeleteItem: Notification.Name {
        return Notification.Name("Delete Item")
    }
}

  1. 在适当的视图上,查找删除消息"


// Receive Message that the Disease should be deleted
    .onReceive(NotificationCenter.default.publisher(for: .didSelectDeleteDisease)) {_ in

        //1: Dismiss the View (IF It also contains Data from the Item!!)
        self.presentationMode.wrappedValue.dismiss()

        //2: Start deleting Disease - AFTER view has been dismissed
        DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(1)) {self.dataStorage.deleteDisease(id: self.diseaseDetail.id)}
    }

  1. 在访问某些CoreData元素的视图上保持安全-检查isFault!


    VStack{
         //Important: Only display text if the disease item is available!!!!
           if !diseaseDetail.isFault {
                  Text (self.diseaseDetail.text)
            } else { EmptyView() }
    }

有点黑,但这对我有用.

A little bit hacky, but this works for me.

这篇关于SwiftUI持有对导致崩溃的已删除核心数据对象的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆