问题描述
我正在构建的iOS应用会遍历Core Data中定义的PhraseGroup
对象的列表,并显示与每个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,因为我想将此类链接用于其他功能。
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.
我认为,我的容器和托管对象上下文的设置非常传统。唯一有点不寻常的功能是,我在将托管对象上下文添加到环境的同时,添加了Singleton 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)
关于它的价值,我尝试在SceneDelegate.swift中显式设置context.retainsRegisteredobjects = false
,但这对抑制此错误没有影响。我相信无论如何这都是默认设置。
https://davedelong.com/blog/2018/05/09/the-laws-of-core-data/的警告提示了我在onAppear()中对performAndWait的使用,该警告涉及从不同的受管对象上下文更改“相同”对象的危险,但即使在阅读了有关不继承{{1 }},因为它们已从应用程序的视图层次结构中分离出来,所以我不认为这是这里发生的事情。为了更加安全,我在调用托管对象上下文时将其传递给模态视图,并在调试器中检查模态视图的托管对象上下文显示它不是nil,并且它没有父对象-两者这向我暗示我正在处理“正确的”托管对象上下文。
如果我只能正式地捕获它,是否有可能该异常实际上是良性的?如果是这样,我如何以及在哪里可以捕获到异常?另一种方法(大概是最好的方法)是,有没有一种方法可以避免我首先触发此异常?
解决方法
新手错误。发生错误后,无法检查Xcode中的原始异常回溯。完成后,我发现我将在应用程序的初始窗口中设置moc.retainsRegisteredObjects = true
(在我的帖子中未显示)。消除它可以解决问题。