CoreData 调试警告:多个 NSEntityDescriptions 声明冲突的解决

在这里插入图片描述

概述

目前在苹果生态 App 的开发中,CoreData 数据库仍然是大部分中小应用的优先之选。不过,运行时 CoreData 常常产生各种“絮絮叨叨”的警告不禁让初学的秃头小码农们云里雾里。

在这里插入图片描述

这不,对于下面这一大段 CoreData 警告,大家是否一眼便能拔丁抽楔,立即找出真正问题之所在呢?

warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass ‘XXX’ so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass ‘XXX’ so +entity is unable to disambiguate.

别急,我们马上为小伙伴们拨开云雾见青天。

在本篇博文中,您将学到如下内容:

  • 概述
  • 1. 喋喋不休:让人心烦意乱的警告
  • 2. 产生警告的原因:好心办坏事
  • 3. 和警告说拜拜
    • 3.1 抛弃 VictoryStage 返回对象
    • 3.2 指明 VictoryStage 对应的 NSEntityDescription
    • 3.3 虚拟托管对象
  • 总结

闲言少叙,Let‘s find out!!!😉


1. 喋喋不休:让人心烦意乱的警告

首先,让我们看一下示例中 CoreData 容器的创建代码:

struct PersistenceController {// CoreData 持久存储容器static let shared = PersistenceController()// Xcode 预览使用的 CoreData 容器,永远只在内存中玩耍😁static let preview = PersistenceController(inMemory: true)let container: NSPersistentCloudKitContainerinit(inMemory: Bool = false) {container = NSPersistentContainer(name: "Store")if inMemory {container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")}container.loadPersistentStores(completionHandler: { (storeDescription, error) inif let error = error as NSError? {fatalError("Unresolved error \(error), \(error.userInfo)")}})}
}

可以看到上面的代码平淡无奇。我们为 App 和 Xcode 预览分别创建了对应的持久存储容器(NSPersistentContainer),对于后者我们只需将存储路径指向一个“黑洞”(“/dev/null”)即可,这也是 Apple 官方标准的处理方法。

接着,简单介绍一下我们 App 数据库的表结构:

  • Project 托管类存放日常项目,它含有多个 VictoryStage 对象,它们之间是一对多的关系;
  • VictoryStage 托管类用来存放每个项目的执行情况;

运行 App 进入业务界面后,恼人的警告“大军”立即接踵而来:

warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass ‘VictoryStage’ so +entity is unable to disambiguate.


CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass ‘VictoryStage’ so +entity is unable to disambiguate.


warning: ‘VictoryStage’ (0x600003508630) from NSManagedObjectModel (0x600002128a50) claims ‘VictoryStage’.


CoreData: warning: ‘VictoryStage’ (0x600003508630) from NSManagedObjectModel (0x600002128a50) claims ‘VictoryStage’.
warning: ‘VictoryStage’ (0x6000035048f0) from NSManagedObjectModel (0x60000217d400) claims ‘VictoryStage’.


CoreData: warning: ‘VictoryStage’ (0x6000035048f0) from NSManagedObjectModel (0x60000217d400) claims ‘VictoryStage’.
error: +[VictoryStage entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass


CoreData: error: +[VictoryStage entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass

仔细观察警告内容,它们大致在述说着同一个 NSManagedObject 托管类型被多个 NSEntityDescription 所声明,这个托管类型就是上面的 VictoryStage 类。

2. 产生警告的原因:好心办坏事

上面警告只是出现在 Xcode 的调试控制台里,并没有引起 App 执行的中断。貌似它的严重性并没有那么大。但是,要注意在这种情况下 CoreData 的存储操作可能会产生未定义行为,所以更需严肃对待。

因为警告发生在 SwiftUI 的视图中,所以我们可以立即采用“最小系统法”让罪魁祸首“原形毕露”。

很快,我们就将产生的警告定位到如下方法中:

func calcCurrentVStage() throws -> VictoryStage {let req = VictoryStage.fetchRequest()req.predicate = .init(format: "project == %@", self)req.sortDescriptors = [.init(keyPath: \VictoryStage.end, ascending: false)]req.fetchLimit = 1// 产生一个 VictoryStage 对象用来临时保存一次性结果let dumpContext = PersistenceController.preview.container.viewContextlet dumpVStage = VictoryStage(context: dumpContext)dumpVStage.start = currentStageStart!dumpVStage.end = nildumpVStage.daysElapsed = 0if let recentVStage = try managedObjectContext?.fetch(req).first {if let lastSetback = newestSetbacks(1).first, lastSetback.date! < recentVStage.realEnd  {return recentVStage} else {return dumpVStage}} else {return dumpVStage}
}

该方法主要做了以下几件事:

  • 计算 Project 当前的 VictoryStage 对象;
  • 当无法找到当前 VictoryStage 对象时,我们会产生一个临时 VictoryStage 对象,然后返回它;
  • 我们希望该临时 VictoryStage 对象永远驻留在内存里,所以将它的上下文设置为前面为 Xcode 预览特供的 “In Memory Only” 上下文;

我们的本意是好的,可惜好心办了坏事。

在这里插入图片描述

考虑一下,当执行 calcCurrentVStage() 方法后,当前我们就同时有了两个 CoreData 持久存储容器(Container):shared 和 preview。其中,每个 Container 中都会有一个对应 VictoryStage 的实体描述对象(NSEntityDescription) ,它们重复了!这就是博文开头警告的根本原因!

再回顾一下上面的代码:

let dumpContext = PersistenceController.preview.container.viewContext
let dumpVStage = VictoryStage(context: dumpContext)

可以看到,我们创建的 dumpVStage 对象无法确定是对应 shared 或 preview 哪个容器中的 VictoryStage 描述符,这自然让 CoreData 在运行时方寸大乱。

那么,我们又该何去何从呢?

3. 和警告说拜拜

3.1 抛弃 VictoryStage 返回对象

一种解决办法是完全抛弃临时的 VictoryStage 对象,只返回一个包含 VictoryStage 对象属性的元组,毕竟我们也不是真的要读取 VictoryStage 对象,而只是想访问它内部属性的值。

3.2 指明 VictoryStage 对应的 NSEntityDescription

除此之外,我们还可以向 CoreData 运行时(Runtime)“忠告善道”指定对应的 NSEntityDescription 对象:

let previewContainer = PersistenceController.preview.container        
let vstageEntity = previewContainer.managedObjectModel.entitiesByName["VictoryStage"]!
let dumpVStage = VictoryStage(entity: vstageEntity, insertInto: previewContainer.viewContext)

在上面的代码中,我们利用托管类的另一个构造器 init(entity:insertInto:) 为 dumpVStage 临时对象指明了对应的 entity 对象,并同时将其与 previewContainer.viewContext 上下文相绑定。

3.3 虚拟托管对象

既然我们的目的是创建一个只驻留于内存中的 VictoryStage 临时对象,那么为什么不再大胆一些,直接忽略 preview 容器,只借助 shared 创一个虚拟托管对象呢?

所谓虚拟托管对象就是永远不会保存到持久存储中的对象,利用这一点我们可以轻松重制上面的 dumpVStage 临时对象了:

let vstageEntity = PersistenceController.shared.container.managedObjectModel.entitiesByName["VictoryStage"]!
let dumpVStage = VictoryStage(entity: vstageEntity, insertInto: nil)

如您所见,现在 dumpVStage 对象将不会与任何上下文绑定,所以它永远不会被存储到持久数据库中。


注意,我们不能这样直接创建 VictoryStage 虚拟托管对象:

let dumpVStage = VictoryStage()

虽然编译没有任何问题,但运行会妥妥的崩溃哦,切记切记。


至此,我们完全解惑了博文开头那个警告,小伙伴们想必再遇到类似的问题一定能够胸有成竹啦,棒棒哒!💯

总结

在本篇博文中,我们讨论了 “Multiple NSEntityDescriptions claim the NSManagedObject subclass” 这一多个 NSEntityDescriptions 声明冲突警告产生的原因,并给出多个解决方案。

感谢观赏,再会啦!😎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35081.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决QT_Debug 调试信息不输出问题

方式1 &#xff1a;手动通过添加环境变量解决 ->使用命令&#xff1a; QT_LOGGING_TO_CONSOLE1 qtcreator启动 ->如若还未输出qDebug调试信息 则在程序中引<QLoggingCategory>包 #include <QLoggingCategory> ->在程序入口添加 QLoggingCategory::defa…

【CF】Day9——Codeforces Round 953 (Div. 2) BCD

B. New Bakery 题目&#xff1a; 思路&#xff1a; 被标签害了&#xff0c;用什么二分&#xff08; 很简单的思维题&#xff0c;首先如果a > b&#xff0c;那么全选a就行了&#xff0c;还搞啥活动 否则就选 b - a 天来搞活动&#xff0c;为什么&#xff1f; 首先如果我…

[MAVEN][经验总结]MAVEN_HOME和M2_HOME的配置建议

前言 MAVEN_HOME和M2_HOME都是maven的环境变量&#xff0c;要配置哪个&#xff0c;与maven版本有关&#xff0c;我在实操过程中遇到相关的问题&#xff0c;现记录如下。 MAVEN_HOME和M2_HOME的区别 MAVEN_HOME 和 M2_HOME 本质上是同一个作用的环境变量&#xff0c;它们的区…

力扣Hot100——169. 多数元素

解法1&#xff1a;使用HashMap 将nums数组映射到HashMap中&#xff0c;键为nums的值&#xff0c;值为nums中值的数量&#xff1b; 然后遍历哈希表&#xff0c;返回值最大的键 class Solution {private Map<Integer, Integer> countNums(int[] nums) {Map<Integer, Int…

EasyRTC嵌入式音视频通话SDK:微信生态支持、轻量化架构与跨平台兼容性(Linix/Windows/ARM/Android/iOS/LiteOS)

随着WebRTC技术的不断发展&#xff0c;实时音视频通信在各个领域的应用越来越广泛。EasyRTC嵌入式音视频通话SDK作为一款基于WebRTC技术的实时通信解决方案&#xff0c;凭借其强大的功能和灵活的集成能力&#xff0c;受到了越来越多开发者的关注。 一、系统架构设计 纯C语言开…

QuickAPI:一键将 Excel 数据转为数据库表

在开发和数据管理中&#xff0c;将 Excel 数据快速导入数据库是一项常见需求&#xff0c;但手动建表和导入的过程往往让人头疼。 QuickAPI 作为一款高效的统一数据服务平台&#xff0c;提供了一键将 Excel 数据转为数据库表的功能&#xff0c;极大简化了操作流程。本文将以技术…

【MySQL】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法

在DQL的基础查询中&#xff0c;我们已经学过了多表查询的一种&#xff1a;联合查询&#xff08;union&#xff09;。本文我们将系统的讲解多表查询。 笛卡尔积现象 首先&#xff0c;我们想要查询emp表和stu表两个表&#xff0c;按照我们之前的知识栈&#xff0c;我们直接使用…

JavaScript如何做类型转换

一、类型转换 二、补充 console.log(1 "2" "2"); // 122 console.log(1 "2" "2"); // 32 console.log(1 -"1" "2"); // 02 console.log("1" "1" "2"); // 112 consol…

华为中小型企业项目案例

实验目的(1) 熟悉华为交换机和路由器的应用场景 (2) 掌握华为交换机和路由器的配置方法 实验拓扑实验拓扑如图所示。 华为中小型企业项目案例拓扑图 实验配置市场部和技术部的配置创建VLANLSW1的配置 [LSW1]vlan batch 10 20 [LSW1]q…

【PyTorch][chapter-35][MLA]

前言&#xff1a; MLA&#xff08;Multi-head Latent Attention&#xff0c;多头潜在注意力&#xff09;旨在提高推理效率和降低计算资源的消。MLA的核心思想在于通过信息转移来优化KV缓存的使用 MLA的技术特点主要包括&#xff1a; KV压缩与潜在变量&#xff1a;将键&#xff…

Spring Cloud 中的服务注册与发现: Eureka详解

1. 背景 1.1 问题描述 我们如果通过 RestTamplate 进行远程调用时&#xff0c;URL 是写死的&#xff0c;例如&#xff1a; String url "http://127.0.0.1:9090/product/" orderInfo.getProductId(); 当机器更换或者新增机器时&#xff0c;这个 URL 就需要相应地变…

微服务存在的问题及解决方案

微服务存在的问题及解决方案 1. 存在问题 1.1 接口拖慢 因为一个接口在并发时&#xff0c;正好执行时长又比较长&#xff0c;那么当前这个接口占用过多的 Tomcat 连接&#xff0c;导致其他接口无法即时获取到 Tomcat 连接来完成请求&#xff0c;导致接口拖慢&#xff0c;甚至…

centos 安装pip时报错 Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64

centos 安装pip时报错 [rootindex-es app-ai]# yum update Loaded plugins: fastestmirror Repository centos-sclo-rh is listed more than once in the configuration Determining fastest mirrors Could not retrieve mirrorlist http://mirrorlist.centos.org?archx86_64…

解决图片转 ICO 图标难题,支持批量处理

还在为图片转 ICO 图标发愁吗&#xff1f;别担心&#xff0c;今天为大家带来一款超实用的工具 ——Any to Icon。它功能强大&#xff0c;可实现批量图片转 ICO 图标&#xff0c;轻松解决格式转换难题。更棒的是&#xff0c;这款工具极为小巧&#xff0c;无需安装&#xff0c;即…

MultiPost--多平台博客发布工具

网站介绍 一键发布内容到多个社交平台的浏览器插件&#xff0c;支持知乎、微博、小红书、抖音等主流平台&#xff0c;支持文字、图片、视频等内容形式. 地址 GitHub &#xff1a; https://github.com/leaper-one/MultiPost-Extension Chorme: https://chromewebstore.google.…

Linux进程状态详解:僵尸进程与孤儿进程的深度探索与实践

文章目录 前言一、进程状态概述1.1 运行状态1.2 阻塞状态1.3 挂起状态 二、具体的Linux操作系统中的进程状态2.1 Linux内核源代码2.2 查看进程状态2.3 D磁盘休眠状态(Disk sleep)D状态的定义&#xff1a; 2.4 T停止状态(stopped)停止状态的概述&#xff1a;停止状态的触发条件&…

【Linux】深入理解进程和文件及内存管理

个人主页~ 深入理解进程和文件及内存管理 一、重谈Linux下一切皆文件二、操作系统对物理内存的管理1、物理内存与磁盘的数据交互2、操作系统对物理内存的管理 三、文件页缓冲区向文件写入数据的过程 四、动态库是如何被加载的关于动态库中的全局变量 五、深入理解地址1、程序地…

★9.4.2 context2D 绘图

返回目录&#xff1a; Qt QML专栏目录结构_qml 项目 目录-CSDN博客 ★9.4.2 context2D 绘图 Object <- context 属性 canvas : QtQuick::Canvas fillRule : enumeration fillStyle : variant fillStyle: 设置或获取当前填充颜色或样式。 font : string g…

汇编基础知识

CPU&#xff1a;一种可以执行机器指令进行运算的芯片&#xff08;微处理器&#xff09;。 存储器&#xff08;内存&#xff09;&#xff1a;存放CPU可以工作的指令和数据&#xff08;指令和数据都是二进制信息&#xff09;。 磁盘不同于内存&#xff0c;磁盘中的数据要读到内…

1536数字三角形

1536数字三角形 ⭐️难度&#xff1a;中等 &#x1f31f;考点&#xff1a;动态规划 &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {public static void main(…