CoreData问题:绑定非可选NSAttributedString删除实体时崩溃
CoreData非可选NSAttributedString绑定UITextView删除实体崩溃问题
问题说明
从CoreData获取非可选的NSAttributedString,将其作为Binding传递给UIKit的UITextView使用时,其他操作均正常,但调用context.delete(entity)删除持有该属性的实体时应用崩溃,错误触发_assertionFailure。
测试发现的关键现象:
- 使用自定义计算型
Binding<NSAttributedString>传递绑定值时不会崩溃; - 将
NSAttributedString改为可选类型NSAttributedString?后,使用$语法传递绑定值不会崩溃; - 使用String类型时,无论哪种绑定方式都不会崩溃。
崩溃原因
当使用$entity.fancyText这种自动生成的KeyPath绑定时,删除实体后SwiftUI的属性图系统仍会尝试访问已被销毁的CoreData实体的非可选属性。由于实体已被标记为删除,底层无法返回有效的非可选NSAttributedString值,直接触发断言失败。
而自定义Binding会在getter中显式访问entity.fancyText,删除后实体的ObservedObject会通知视图更新,此时视图通常已被销毁或不再渲染,避免了无效访问;可选类型则允许返回nil,不会触发非可选断言。
解决方案
方式1:使用自定义计算型Binding(已验证有效)
保持CoreData属性为非可选,在视图中用自定义Binding替代自动生成的$绑定:
private var fancyTextBinding: Binding<NSAttributedString> { Binding<NSAttributedString> ( get: { entity.fancyText }, set: { newValue in entity.fancyText = newValue } ) } // 在body中使用 UITextPort(text: fancyTextBinding)
方式2:将CoreData属性改为可选类型
修改Entity中的属性定义为可选,并确保初始化或数据写入时提供默认值:
@NSManaged public var fancyText: NSAttributedString?
同时保留UITextView回调中的nil处理逻辑:
func textViewDidChange(_ textView: UITextView) { text = textView.attributedText ?? NSAttributedString() }
完整修复代码示例
EntityView.swift
struct EntityView: View { @Environment(\.managedObjectContext) var context @ObservedObject var entity: Entity private var fancyTextBinding: Binding<NSAttributedString> { Binding<NSAttributedString> ( get: { entity.fancyText }, set: { newValue in entity.fancyText = newValue } ) } var body: some View { VStack { HStack { UITextPort(text: fancyTextBinding) Spacer() Button("Delete") { context.delete(entity) // 建议添加上下文保存,确保状态及时更新 try? context.save() } } Divider() } } }
UITextPort.swift
struct UITextPort: UIViewRepresentable { @Binding var text: NSAttributedString func makeCoordinator() -> Coordinator { Coordinator(text: $text) } func makeUIView(context: Context) -> UITextView { context.coordinator.editor } func updateUIView(_ editor: UITextView, context: Context) { editor.attributedText = text } class Coordinator: NSObject, UITextViewDelegate { @Binding var text: NSAttributedString init(text: Binding<NSAttributedString>) { self._text = text } lazy var editor: UITextView = { let editor = UITextView() editor.delegate = self return editor }() func textViewDidChange(_ textView: UITextView) { text = textView.attributedText ?? NSAttributedString() } } }
Entity.swift
class Entity: NSManagedObject { public static func fetchRequest() -> NSFetchRequest<Entity> { NSFetchRequest<Entity>(entityName: "Entity") } @NSManaged public var fancyText: NSAttributedString } extension Entity : Identifiable {}
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

