0

我正在构建的 iOS 应用程序遍历PhraseGroupCore Data 中定义的对象列表,并显示extraText与每个PhraseGroup. 这显示了如何PhraseGroup定义:

extension PhraseGroup {

@nonobjc public class func fetchRequest() -> NSFetchRequest<PhraseGroup> {
    return NSFetchRequest<PhraseGroup>(entityName: "PhraseGroup")
}

@NSManaged public var extraText: String
@NSManaged public var phraseGroupID: UUID
@NSManaged public var text: String
@NSManaged public var phrase: NSSet?
@NSManaged public var piece: Piece

我希望用户能够长按extraText列表中的任何条目,然后在模式表中编辑此字段。我不想为此使用 NavigationLink,因为我想将这样的链接用于其他功能。

下面显示了我如何列出 PhraseGroups,以及我如何显示模式表:

import SwiftUI
import CoreData

struct PhraseGroupView: View {

@Environment(\.managedObjectContext) var moc
@Binding var phraseGroupViewAlertItem: AlertItem?
@State private var isEditMode: EditMode = .inactive
@State private var phraseGroupObjectID: NSManagedObjectID? = nil

private var fetchRequest: FetchRequest<PhraseGroup>
private var phraseGroups: FetchedResults<PhraseGroup> { fetchRequest.wrappedValue }

var body: some View {
NavigationView {
    VStack(spacing: 20){
        Section {
            List {
                ForEach (phraseGroups, id: (\PhraseGroup.phraseGroupID)) { phraseGroup in
                    HStack {
                        Text("\(phraseGroup.wrappedExtraText)")
                    }
                    .onLongPressGesture {
                        phraseGroupObjectID = phraseGroup.objectID
                    }
                }
                .onDelete(perform: delete)
            }
            .sheet(item: self.$phraseGroupObjectID) { objID in
                TestPhraseGroupEditView(phraseGroupObjectID: objID).environment(\.managedObjectContext, self.moc)
            }
        }
    }
    .navigationBarTitle("This is phraseGroup navBarTitle", displayMode: .inline)
    .navigationBarItems(leading:
                            HStack {
                                Button(action: {
                                    // yet to come
                                }) {
                                    Image(systemName: "plus").resizable()
                                        .frame(width: 16, height: 16)
                                        .aspectRatio(contentMode: .fit)
                                        .foregroundColor(.myKeyColor)
                                }
                            }, trailing:
                                HStack {
                                    EditButton()
                                        .frame(width: 60, height: 20)
                                        .aspectRatio(contentMode: .fit)
                                        .foregroundColor(.myKeyColor)
                                })
    .environment(\.editMode, self.$isEditMode)
  }
}

init (phraseGroupViewAlertItem: Binding<AlertItem?>, piece: Piece) {
    self._phraseGroupViewAlertItem = phraseGroupViewAlertItem
    fetchRequest = FetchRequest<PhraseGroup>(
        entity: PhraseGroup.entity(),
        sortDescriptors: [
            NSSortDescriptor(key: "extraText", ascending: true)
        ],
        predicate: NSPredicate(format: "piece == %@", piece)
        // 'piece' in the predicate above is the name of a Piece <- PhraseGroup relationship defined in Core Data
    )
}

这是我的模态表(目前非常简单,还没有尝试包含保存功能):

import SwiftUI
import CoreData

  struct TestPhraseGroupEditView: View {

@Environment(\.managedObjectContext) var moc
@State private var extraTextForEditing = ""
var phraseGroupObjectID: NSManagedObjectID!

var phraseGroup: PhraseGroup {
        moc.object(with: phraseGroupObjectID) as! PhraseGroup
    }

var body: some View {
    NavigationView {
        Form {
            Section {
                TextField("Extra Text", text: $extraTextForEditing)
            }
        }
    }
    .onAppear {
        phraseGroup.managedObjectContext!.performAndWait {
            extraTextForEditing = phraseGroup.extraText
        }
    }
  }
}

长按上一个列表后,此表显示正常。但是,当我打开 TextField 进行输入时,应用程序会抛出NSInternalInconsistencyException原因An NSManagedObjectContext's retain policy cannot be changed while it has registered objects. Trying using reset() first.

我相信,我的容器和托管对象上下文的设置非常传统。唯一稍微不寻常的功能是,我在将托管对象上下文添加到环境的同时,添加了一个单例 PiecePlayer(我的应用程序的音频管理类)作为环境对象。

这是来自 AppDelegate.swift 的容器部分:

lazy var persistentContainer: NSPersistentContainer = {

    let container = NSPersistentContainer(name: "AK6")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {

            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

这是 SceneDelegate.swift 中的上下文设置:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
var pp = PiecePlayer()
@Environment(\.managedObjectContext) var moc

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    let contentView = ContentView().environment(\.managedObjectContext, context).environmentObject(pp)

对于它的价值,我尝试context.retainsRegisteredObjects = false在 SceneDelegate.swift 中显式设置,但这对抑制此错误没有影响。我相信无论如何这是默认设置。

我在 onAppear() 中使用 performAndWait 是由https://davedelong.com/blog/2018/05/09/the-laws-of-core-data/上关于更改“相同”对象的危险的警告提示的不同的托管对象上下文,但即使在阅读了关于模式视图没有继承的报告后,Environment(\.managedObjectContext)因为它们与应用程序的视图层次结构分离,我不相信这就是这里发生的事情。为了更加安全,我在调用它时将托管对象上下文传递给模式视图,并在调试器中检查模式视图的托管对象上下文显示它不是 nil,并且它没有父级 - 两者这向我表明我正在处理“正确的”托管对象上下文。

如果我只能正式捕获它,这个异常是否可能实际上是良性的?如果是这样,我如何以及在哪里可以捕获异常?或者(并且可能最好),有没有办法我可以避免首先触发这个异常?

4

1 回答 1

0

新手错误。发生错误后无法检查 Xcode 中的原始异常回溯。当我这样做时,我发现我已经moc.retainsRegisteredObjects = true在应用程序的初始窗口中进行了设置(未在我的帖子中显示)。删除这个解决了这个问题。

于 2020-09-27T01:49:33.777 回答