消失的它:揭开 CoreData 托管对象神秘的消失之谜(上)

news2025/5/18 4:21:56

在这里插入图片描述

概述

使用 CoreData 作为 App 持久存储“定海神针”的小伙伴们想必都知道,我们需要将耗时的数据库查询操作乖巧的放到后台线程中,以便让主线程负责的 UI 获得风驰电掣般地享受。

在这里插入图片描述

不过,如何将后台线程中查询获得的托管对象稳妥的传送至主线程中,这却是一个问题。稍有不慎,原本“老实本分的”托管对象可能会立即消失的无影无踪,让一切前功尽弃。

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

  • 概述
  • 1. 一次“平淡无奇”的优化
  • 2. 阿欧!对象全部不见鸟
  • 总结

相信学完本系列课程后,小伙伴们倘若再遇到类似的问题都会胸有成竹,迎刃而解。

那还等什么呢?让我们马上开始探案大冒险吧,Let‘s go!!!😉


1. 一次“平淡无奇”的优化

小伙伴们都知道,要想在苹果系统中更新界面内容,我们必须在主线程中勇敢的承担起所有责任。

这在 SwiftUI 中也不例外。何曾几时,Apple 已将 UIKit 以及 SwiftUI 所有与界面相关的对象都用 MainActor 修饰起来,从而彰显出主线程这一神圣使命。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

于是乎,我们必须将所有耗时操作都统统放到后台线程中以便让主线程“北窗高卧”。那么,哪些属于耗时操作呢?CoreData 海量数据的查询妥妥的算是一个。

在下面的代码中,视图里的 countTraces 属性包含了一切图表(Chart)所需的计数记录(CountTrace)。毫无疑问,它已被牢牢打上 MainActor 的烙印,这不禁让我们必须牢记——把对它的任何更新操作都放到主线程上去:

@State private var countTraces = [CountTrace]()

Chart {
    ForEach(Array(countTraces.enumerated()), id: \.offset) { i, trace in
        PointMark(x: .value("索引", i), y: .value("计数", trace.count))
    }
    
    ForEach(Array(countTraces.enumerated()), id: \.offset) { i, trace in
        LineMark(x: .value("索引", i), y: .value("计数", trace.count))
            .foregroundStyle(.blue.opacity(0.33))
    }
}

当用户触发显示图表的操作时,我们从后台线程查询这些计数记录,然后再回到主线程里将它们赋予 countTraces 属性:

let container = Model.shared.controller.container
container.performBackgroundTask { bgContext in
	// 获取最近 30 条计数记录
    let traces = try! counter.querySortedTraces(30, context: bgContext)
    
    DispatchQueue.main.sync {
    	// 回到主线程中更新 countTraces 属性
        countTraces = traces
    }
}

这些都看起来是那么的驾轻就熟,不是吗?

2. 阿欧!对象全部不见鸟

不过,当我们意气风发地在 Xcode 预览中执行上述代码时,就会发现结果似乎有些不太对劲:

在这里插入图片描述

在显示的图表中所有记录的计数都一样,这貌似不大可能。我们可以完全肯定这些计数值都是随机生成的,所以它们全部相同的概率几乎为零。

俗话说得好:不会调试的厨师不是好司机。为了让真相逐渐浮出水面,我们首先需要对之前的实现增加调试辅助代码:

let container = Model.shared.controller.container
container.performBackgroundTask { bgContext in
    let traces = try! counter.querySortedTraces(30, context: bgContext)
    print("#1: \(traces.map {$0.count})")
    
    DispatchQueue.main.async {
        print("#2: \(traces.map {$0.count})")
        countTraces = traces
    }
}

接着,重新在 Xcode 预览中触发图表展开操作,从调试控制台的输出可以看到,前脚我们获取到的所有 CountTrace 对象都表现正常,而后一秒它们将会让我们瞠目结舌:

#1: [10, 1, 7, 4, 3, 4, 4, 7, 8, 3, 5, 6, 8, 3, 3, 2, 2, 10, 3, 9, 7, 1, 9, 1, 7, 6, 4, 8, 1, 8]
#2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

从上面的调试输出不难发现:进入主线程后,traces 所有对象的 count 属性竟然全部“神秘地”变为 0 了。

在这里,请允许我们先猜测一下问题的根本症结:之所以会如此,是因为 traces 中所有托管对象被提前释放掉了。

至于为什么它们会被释放,我们在后面再给出真正的原因。

在下一篇文章中,大家将基于我们的推测来尝试解决一下这个问题,当然这些尝试都将先以失败而告终。

我们不见不散!

总结

在本篇博文中,我们介绍了 SwiftUI 后台线程向主线程传递托管对象“神秘失踪”这一迷案,并对其缘由给出了初步猜测。

感谢观赏,下一篇再见!😎

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电流互感器的两相星形接线的建模与仿真

微♥“电击小子程高兴的MATLAB小屋”获取巨额优惠 1.模型简介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2016Rb)软件。建议采用matlab2016 Rb及以上版本打开。(若需要其他版本可联系代为转换) 2.仿真模型 3.仿真结果 3.1一次…

Day3—循环起来吧

第一章一切从安装有利工具开始 第二章Python基本语法 第三章结构化程序设计 3.1条件语句 3.1.1if语句 只需要判断一个条件,并且在该条件为 True 时执行特定的代码块,而在条件为 F…

forms+windows添加激活水印

formswindows添加激活水印 多语言水印文本,根据系统语言自动切换。水印显示在每个屏幕的右下角,位置动态调整。半透明灰色文字,微软雅黑字体。窗口无边框、置顶、透明背景,不干扰用户操作。支持多显示器。高DPI适配。 效果图&am…

Linux安装postgresql17

1、下载 wget https://ftp.postgresql.org/pub/source/v17.4/postgresql-17.4.tar.gz 2、上传解压 tar -zxvf postgresql-17.4.tar.gz 3、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel …

从零搭建微服务项目Pro(第0章——微服务项目脚手架搭建)

前言: 在本专栏Base第0章曾介绍一种入门级的微服务项目搭建,尽管后续基于此框架上实现了Nacos、Eureka服务注册发现、配置管理、Feign调用、网关模块、OSS文件存储、JSR参数校验、LogBack日志配置,鉴权模块、定时任务模块等,但由于…

聊一聊原子操作和弱内存序

1、原子操作概念 在并发编程中,原子操作(Atomic Operation)是实现线程安全的基础机制之一。从宏观上看,原子操作是“不可中断”的单元,但若深入微观层面,其本质是由底层处理器提供的一组特殊指令来保证其原…

Ubuntu中部署MeloTTS

0. 环境 ubuntu server 22.04 cuda version 12.4 python version 3.9 1. 安装python依赖 git clone https://github.com/myshell-ai/MeloTTS.git cd MeloTTS注意不是执行 pip install melotts 如果国内服务器无法从github中下载源码,那么可以把github改为gitc…

Adobe After Effects的插件--------Optical Flares之Options概述

Optical Flares插件的Options是对整个效果的组装和设置。点击该按钮会弹出一个组装室弹窗。 Options组装室就是对每个【镜头对象】进行加工处理,再将其组装在一起,拼凑成完整的光效。 接下来是我对组装室的探索: 面板 面板中有预览、堆栈、编辑和浏览按钮,其作用是调节窗…

字符串与相应函数(上)

字符串处理函数分类 求字符串长度:strlen长度不受限制的字符串函数:strcpy,strcat,strcmp长度受限制的字符串函数:strncpy,strncat,strncmp字符串查找:strstr,strtok错误信息报告:strerror字符操作,内存操作函数&…

Laravel源码进阶

Laravel源码进阶 版本 laravel5.8 生成服务容器 public index.php //compose必要操作 require __DIR__./../vendor/autoload.php; //容器文件 $app require_once __DIR__./../bootstrap/app.php;-bootstrap/app.php //初始化容器 构造函数中执行这个几个方法 //$this->…

镜舟科技亮相 2025 中国移动云智算大会,展示数据湖仓一体创新方案

4月10-11日,2025 中国移动云智算大会在苏州金鸡湖国际会议中心成功举办。大会以“由云向智,共绘算网新生态”为主题,汇聚了众多行业领袖与技术专家,共同探讨了算力网络与人工智能的深度融合与未来发展趋势。 作为中国领先的企业级…

2025蓝桥杯省赛C/C++研究生组游记

前言 至少半年没写算法题了,手生了不少,由于python写太多导致行末老是忘记打分号,printf老是忘记写f,for和if的括号也老是忘写,差点连&&和||都忘记了。 题目都是回忆版本,可能有不准确的地方。 …

重读《人件》Peopleware -(6)Ⅰ管理人力资源Ⅴ-帕金森定律重探 Parkinson’s Law Revisited

1954年,英国作家C. Northcote Parkinson引入了一个概念:工作会膨胀以填满分配给它的时间,这个概念现在被熟知为帕金森定律。如果你不知道很少有管理者接受过任何管理培训的话,你可能会以为他们都参加过一个关于帕金森定律及其影响…

Linux-内核驱动-led

登记设备号(后面可以动态分配) 自己定义内核函数 登记设备名字和功能 exit和init在内核启动自动执行 这样定义直接操作物理地址 ioctl 定义了设备文件的各种操作,并准备将其注册到内核中。 代码中声明了一个cdev结构体变量cdev,这…

记录一次因ASM磁盘组空间不足,导致MAP进程无法启动

生产中 ADG 库出现告警,检查发现 map 进程异常: 检查 alter 日志,出现: ORA-19504:failed to create file "DATAC1/casarch/2_162186_1067953047.arc" ORA-17502:ksfdcre:4 Failed to create file ... ORA-15041:diskgroup "DATAC1" space exhausted OR…

可能存在特殊情况,比如控制台显示有延迟、缓冲问题等影响了显示顺序。

从控制台输出看,正常逻辑应是先执行 System.out.println(" 未处理异常演示 "); 输出对应文本,再因 arr 为 null 访问 length 触发 NullPointerException 输出异常信息。可能存在特殊情况,比如控制台显示有延迟、缓冲问题等影响…

c++中继承方面的知识点

继承的概念及定义 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保 持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象 程序设计的层次结…

PyTorch 学习笔记

环境:python3.8 PyTorch2.4.1cpu PyCharm 参考链接: 快速入门 — PyTorch 教程 2.6.0cu124 文档 PyTorch 文档 — PyTorch 2.4 文档 快速入门 导入库 import torch from torch import nn from torch.utils.data import DataLoader from torchvision …

Spring AI 结构化输出详解

一、Spring AI 结构化输出的定义与核心概念 Spring AI 提供了一种强大的功能,允许开发者将大型语言模型(LLM)的输出从字符串转换为结构化格式,如 JSON、XML 或 Java 对象。这种结构化输出能力对于依赖可靠解析输出值的下游应用程…

从零开始的C++编程 2(类和对象下)

目录 1.构造函数初始化列表 2.类型转换 3.static成员 4.友元 5.内部类 6.匿名对象 1.构造函数初始化列表 ①之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅式,就是初始化列表,初始化…