You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:18:10