iOS 13.4 CoreData SwiftUI 应用程序在设备上因“EXC_BREAKPOINT (code=1, subcode=0x1f3751f08)”崩溃

nin*_*nes 6 core-data swiftui

一个非常简单的 CoreData 应用程序:下面提供了所有代码。

  • 使用 CoreData 模板单视图应用程序启动。
  • 2 个实体,每个实体都有一个字符串属性:Message(title) 和 Post(name)

一个 NavigationView 包含

  • 导航链接到消息列表
  • 导航链接到帖子列表

每个链接的 ListView (Message/Post) 都有

  • 将项目添加到列表的按钮
  • 从列表中删除所有项目的按钮

现在,当您在模拟器(任何 iOS 13.x 版本)上运行此应用程序时,所有这些都按上述描述的预期运行。

但是在运行 iOS 13.4 的设备上

  • 点按“消息”
  • 创建/删除消息工作正常,SwiftUi 视图会立即更新。
  • 点击“返回”
  • 再次点击“消息”。虽然仍然创建/删除消息工作正常:调试器现在显示警告:“环境中的上下文未连接到持久存储协调器:NSManagedObjectContext:0x280ed72c0
  • 点击“帖子”==> 应用程序崩溃并显示 EXC_BREAKPOINT(代码=1,子代码=0x1f3751f08)

您也可以先从 Posts 开始这个过程。然后在消息列表视图上发生同样的崩溃。

我坚信这是一个 iOS 13.4 错误,因为类似的代码在 Xcode 11.3 / iOS 13.3 上运行良好。

有谁知道解决方法或解决方法吗?

这是完整项目的链接:Full Xcode Project

内容视图:

import SwiftUI
import CoreData


struct MessageList: View {
  @Environment(\.managedObjectContext) var moc
  @FetchRequest(entity: Message.entity(), sortDescriptors: [])
  var messages: FetchedResults<Message>

  var body: some View {
    List() {
      ForEach(messages, id: \.self) { message in
        Text(message.title ?? "?")
      }
    }
    .navigationBarItems(trailing:
      HStack(spacing: 16) {
        Button(action: deleteMessages) {
          Image(systemName: "text.badge.minus")
        }
        Button(action: addMessage) {
          Image(systemName: "plus.app")
        }
      }
    )
  }
  func addMessage() {
    let m = Message(context: moc)
    m.title = "Message: \(Date())"
    try! moc.save()
  }
  func deleteMessages() {
    messages.forEach {
      moc.delete($0)
    }
  }
}

struct PostList: View {
  @Environment(\.managedObjectContext) var moc
  @FetchRequest(entity: Post.entity(), sortDescriptors: [])
  var posts: FetchedResults<Post>

  var body: some View {
    List {
      ForEach(0..<posts.count, id: \.self) { post in
        Text(self.posts[post].name ?? "?")
      }
    }
    .navigationBarItems(trailing:
      HStack(spacing: 16) {
        Button(action: deletePosts) {
          Image(systemName: "text.badge.minus")
        }
        Button(action: addPost) {
          Image(systemName: "plus.app")
        }
      }
    )
  }
  func addPost() {
    let p = Post(context: moc)
    p.name = "Post \(UUID().uuidString)"
    try! moc.save()
  }
  func deletePosts() {
    posts.forEach {
      moc.delete($0)
    }
    try! moc.save()
  }
}


struct ContentView: View {
  @Environment(\.managedObjectContext) var moc

  var body: some View {
    NavigationView {
      VStack(alignment: .leading){
        NavigationLink(destination: MessageList()) {
          Text("Messages")
        }.padding()
        NavigationLink(destination: PostList()) {
          Text("Posts")
        }.padding()
        Spacer()
      }
    }.navigationViewStyle(StackNavigationViewStyle())
  }
}

struct ContentView_Previews: PreviewProvider {
  static let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
  static var previews: some View {
    ContentView()
      .environment(\.managedObjectContext, moc)
  }
}
Run Code Online (Sandbox Code Playgroud)

模型截图:

模型

SceneDelegate(未更改模板,提供完整性):

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  var window: UIWindow?

  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)

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
  }

  func sceneDidDisconnect(_ scene: UIScene) {}
  func sceneDidBecomeActive(_ scene: UIScene) {}
  func sceneWillResignActive(_ scene: UIScene) {}
  func sceneWillEnterForeground(_ scene: UIScene) {}
  func sceneDidEnterBackground(_ scene: UIScene) {
    (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
  }
}
Run Code Online (Sandbox Code Playgroud)

AppDelegate(未更改模板,提供完整性):

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    return true
  }

  func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
  }

  func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {}

  // MARK: - Core Data stack

  lazy var persistentContainer: NSPersistentCloudKitContainer = {
    let container = NSPersistentCloudKitContainer(name: "Coredata134")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
      if let error = error as NSError? {
        fatalError("Unresolved error \(error), \(error.userInfo)")
      }
    })
    return container
  }()

  func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
      do {
        try context.save()
      } catch {
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Lob*_*obo 4

更新iOS 14.0(测试版1):

这个问题似乎在 iOS 14 上已经得到解决。


我也相信这是一个错误。

您现在可以通过在 ContentView 的 NavigationLinks 中再次设置环境变量来解决此问题:

NavigationLink(destination: MessageList().environment(\.managedObjectContext, moc)) {
      Text("Messages")
    }.padding()
NavigationLink(destination: PostList().environment(\.managedObjectContext, moc)) {
      Text("Posts")
    }.padding()
Run Code Online (Sandbox Code Playgroud)

编辑:

只是注意到这种解决方法至少有一个严重的负面影响:如果目标视图中的 @FetchRequest 使用 sortDescriptor 并且目标视图本身包含 NavigationLink(例如到 DetailView),则修改 sortDescriptor 中包含的属性一旦新的属性值导致新的排序顺序,DetailView 中的内容将导致 DetailView 被弹出并再次推送。

为了证明这一点:

a) 将名为“value”的整数 16 类型新属性添加到核心数据模型中的消息实体。

b) 更新 func addMessage() 如下:

func addMessage() {
    let m = Message(context: moc)
    m.title = "Message: \(Date())"
    m.value = 0
    try! moc.save()
}
Run Code Online (Sandbox Code Playgroud)

c) 将以下结构添加到 ContentView.swift

struct MessageDetailList: View {
    @ObservedObject var message: Message
    var body: some View {
        Button(action: {
            self.message.value += 1
        }) {
            Text("\(message.title ?? "?"): value = \(message.value)")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

d) 更新 struct MessageList 中的 ForEach,如下所示:

ForEach(messages, id: \.self) { message in
        NavigationLink(destination: MessageDetailList(message: message).environment(\.managedObjectContext, self.moc)) {
            Text("\(message.title ?? "?"): value = \(message.value)")
        }
    }
Run Code Online (Sandbox Code Playgroud)

e) 将 MessageList 中的 @FetchRequest 替换为:

@FetchRequest(entity: Message.entity(), sortDescriptors: [NSSortDescriptor(key: "value", ascending: false)])
Run Code Online (Sandbox Code Playgroud)

运行代码并点击“消息”。创建三条消息,然后点击第三条消息。在详细信息视图中,点击按钮。这会将该消息的 value 属性增加到 1,从而将获取结果重新放到 MessageList 上,这将触发详细列表的再次弹出和推送。

在此输入图像描述