由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(二)

news2024/11/17 19:39:20

在这里插入图片描述

概述

从 WWDC 23 开始,苹果推出了崭新的数据库框架 SwiftData。默认在 SwiftData 中所有对数据的操作都会在主线程中进行,稍有不慎就会让 App 变得“鹅行鸭步”

在这里插入图片描述

那么,对于耗时的数据操作我们该如何优雅的面对?又如何让界面与其“一心一力”的同步呢?

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

  • 概述
  • 3. SwiftData 如何在后台改变数据?
  • 4. 如何将后台的更改同步到界面中?
  • 总结

这是本系列第二篇博文。闲言少叙,让我们马上开始 SwiftData 精彩的探究之旅吧!

Let‘s dive in!!!😉


3. SwiftData 如何在后台改变数据?

现在虽然我们已经圆满解决了之前那个崩溃问题,但是 SwiftData 中数据操作的“水还很深”,值得大家进一步“磨砥刻厉”的研究一番。

首先,我们从简单且实用的话题的聊起:SwiftData 如何在后台修改数据?

SwiftData 对于数据的操作是通过模型上下文来完成的,而通过之前的介绍可知:主模型上下文(Main Model Context,以下简称为主上下文)只能在主线程或 MainActor 上修改数据,而私有模型上下文则适合在其它线程或 Actor 中操作数据。

假设这样一种常见的场景:我们的 App 要在启动时生成大量数据,如果将这一操作用主上下文在主线程上执行就会阻塞界面,这在 App 开发中是绝对不能容忍的!

所以,一种方法就是将它们放在私有上下文在后台线程中执行。

将之前 ContentView 视图的代码略作修改,我们现在暂时抛弃 Model 类型,下面所有的代码都只涉及 Item 托管类型:

struct ContentView: View {
    @Environment(\.modelContext) var modelContext
    @Query var items: [Item]
    
    var body: some View {
        VStack {
            if let item = items.first {
                Text(item.name)
            }
        }
        .padding()
        .task {
            Task.detached {
                let modelContext = ModelContext(.preview)                
                let item = Item(name: "\(Int.random(in: 0...10000))")
                modelContext.insert(item)
                
                try! modelContext.save()
            }
        }
    }
}

从上面的代码可以看到,我们在 ContentView 显示时创建了一个包含随机值的 Item,并视图通过 @Query 将其“抓取”到主界面上显示。

值得注意的是,我们还做了下面几件事:

  • 通过 Task.detached 创建了一个“分离”任务以确保“脏活累活”都在后台线程中运行;
  • 使用 ModelContext 构造器创建了一个私有上下文,该上下文一旦创建就会和它处在的线程或 Actor 所绑定;

到目前为止一切都很简单惬意,不是吗?

不过当我们编译运行后,视图中心却空空如也!创建的 Item 跑哪去了呢?

在这里插入图片描述

4. 如何将后台的更改同步到界面中?

其实,后台线程新创建的 Item 托管对象就在那里,只是它还没有被同步到主上下文中而已。

对于目前的情况来说,SwiftUI 中的 @Query 只能自动同步主上下文中数据的改变,私有上下文中的改变却不在此列。这意味着:我们上面在后台线程中新增的 Item 对象并不能及时刷新到界面中。

这该如何是好呢?

一种简单却略显“粗暴”的方式是,在后台线程插入新 Item 对象后立即强制刷新 UI:

struct ContentView: View {
    @Environment(\.modelContext) var modelContext
    @Query var items: [Item]
    @State var refreshID = false
    
    var body: some View {
        NavigationStack {
            VStack {
                List(items) { item in
                    Text(item.name).font(.headline.weight(.heavy))
                }
                .id(refreshID)
            }
            .toolbar {
                ToolbarItem(placement:.topBarTrailing) {
                    Button("New", systemImage: "plus.app") {
                        Task.detached {
                            let modelContext = ModelContext(.preview)
                            
                            let item = Item(name: "\(Int.random(in: 0...10000))")
                            modelContext.insert(item)
                            
                            try! modelContext.save()
                            
                            await MainActor.run {
                                refreshID.toggle()
                            }
                        }
                    }
                    .foregroundStyle(.white)
                    .tint(.green)
                }
            }
        }
    }
}

从上面的代码不难看出,我们每次在后台新插入 Item 对象后立即刷新了 List 视图,这样做会导致 SwiftUI 重新计算 @Query 宏中 items 的内容。

在这里插入图片描述

如此这般,我们即可在界面中及时反映出后台线程里私有上下文所导致的 SwiftData 数据变化了。


更多与 SwiftUI 界面刷新相关内容的介绍,请小伙伴们移步如下链接观赏:

  • Xcode 15 预览 SwiftUI 视图中 @FetchRequest 查询结果不能正确刷新的解决
  • SwiftUI 如何动态条件显示和隐藏 Toolbar 按钮且不做无谓刷新
  • SwiftUI 如何快速识别视图(View)界面的刷新是由哪个状态的改变导致的?
  • SwiftUI 后台刷新多个 Section 导致 global index in collection view 与实际不匹配问题的解决
  • iOS 16 中 CoreData 托管对象发生变化但其衍生 (Derived) 属性在 SwiftUI 中不刷新的解决
  • SwiftUI上下文菜单(Context Menu)内容不随对象状态刷新的解决

虽说手动刷新整个视图可以勉强“得偿所愿”,但它毕竟会对渲染性能造成或多或少的潜在影响。有没有更好的方法呢?

答案是肯定的!

总结

在本篇博文中,我们讨论了如何在后台线程处理 SwiftData 的数据操作,又如何将这些更改同步到界面中去。

在下一篇博文里,我们将会介绍 SwiftData 2.0 中新引入的 History Trace 机制,并用它来更优雅的解决问题。

感谢观赏,再会 😎

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

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

相关文章

开放式耳机的优缺点?有什么推荐吗?四款开放式蓝牙耳机推荐

开放式耳机的优点有很多其实,但是每个东西多多少少都是一把双刃剑,所以缺点当然也是有的。那就先讲它的优点: 首先因为不入耳的设计,耳机不是直接塞入耳道的,所以能让耳道保持“呼吸”,减少长时间佩戴导致…

基于 AT 固件测试 ESP32 设备作为 WiFi AP 模式创建 TCP Server 开启 UART-to-WiFi 透传模式的指令序列

AT 指令序列如下 ATRESTOREATCWMODE2 // Set the Wi-Fi mode toSoftAP.ATCWSAP"ESP32_softAP","1234567890",5,3 // Set softAPATCIPMUX1 // Enable multiple connections for TCP ServerATCIPSERV…

Windows安装使用Docker

配置Dorker环境 启用或关闭windows功能 安装wsl 以管理员身份打开windows PowerShell,安装相关配置 下载docker应用程序 Releases tech-shrimp/docker_installer (github.com) 安装Docker 默认双击程序就开始安装了,要安装在指定位置,提…

Java | Leetcode Java题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isSubsequence(String s, String t) {int n s.length(), m t.length();int[][] f new int[m 1][26];for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; i > 0; i--) {for (int j 0; j…

华为 HCIP-Datacom H12-821 题库 (7)

有需要题库的可以看主页置顶 V群仅进行学习交流 1.配置 VRRP 跟踪物理接口状态的命令是在华为设备上&#xff0c;以下哪一项是配置 VRRP 跟踪物理接口状态的命令&#xff1f; A、track vrrp vrid 1 interface GigabitEthernet0/0/0 B、vrrp vrid 1 track interface GigabitE…

AI 网关基于 IP 地理位置,增强 Prompt 修饰能力

作者&#xff1a;沈鑫糠&#xff0c;来自昆仑数智瑞道云团队&#xff0c;专注于云原生领域产品研发和相关技术。 前言 什么是 Prompt Engineering 提示词工程&#xff08;Prompt Engineering&#xff09;&#xff0c;也被称为上下文提示&#xff08;In-Context Prompting&am…

Elasticsearch之储存原理和优化

存储原理 上篇介绍了在 ES 内部索引的写处理流程&#xff0c;这个流程是在 ES 的内存中执行的&#xff0c;数据被分配到特定的分片和副本上之后&#xff0c;最终是存储到磁盘上的&#xff0c;这样在断电的时候就不会丢失数据。 具体的存储路径可在配置文件 ../config/elastics…

哨兵排序算法

代码展示 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h>#define MAXSIZE 20 //直接排序 typedef struct {int r[MAXSIZE 1];int length; } SqList; int InsertSort(SqList* L) {int i, j;for (i 2; i < L->length; i){if (L-…

10.2 TCP IP模型、IP协议、IPv4、子网掩码

TCP / IP 协议族 IP协议 IPv4地址 IPv4地址分类 子网掩码 子网掩码用来区分 网络地址 和 主机地址 真题 1

1024页 | 20万字详细讲解大数据系统平台设计

大数据引擎系统针对互联网业务系统对海量大数据的分布式存储、计算、 分析挖掘、建模及业务查询需求&#xff0c;通过提供基于分布式数据仓库的离线计算、实 时计算等服务&#xff0c;实现涵盖数据全生命周期的完整数据处理。大数据服务分系统主 要任务是构建大数据仓库&#x…

IP风险画像如何维护网络安全

在当今数字化时代&#xff0c;互联网已成为我们生活、工作不可或缺的一部分。然而&#xff0c;随着网络应用的日益广泛&#xff0c;网络安全问题也日益凸显。为了有效应对网络安全挑战&#xff0c;IP风险画像技术应运而生&#xff0c;正逐步成为构建网络安全新防线的重要工具。…

华为云征文|华为云Flexus X实例docker部署MinIO对象存储系统obs

华为云征文&#xff5c;华为云Flexus X实例docker部署MinIO对象存储系统obs 什么是华为云Flexus X实例 华为云Flexus X实例云服务是新一代开箱即用、体验跃级、面向中小企业和开发者打造的高品价比云服务产品。Flexus云服务器X实例是新一代面向中小企业和开发者打造的柔性算力…

深度学习从入门到精通——基于unet++算法实现细胞分割

模型定义 import torch from torch import nn__all__ [UNet, NestedUNet]class VGGBlock(nn.Module):def __init__(self, in_channels, middle_channels, out_channels):super().__init__()self.relu nn.ReLU(inplaceTrue)self.conv1 nn.Conv2d(in_channels, middle_channe…

matlab实现简单的保角变换

用虚数的思想可以在虚坐标系内绘制圆&#xff0c;同样可以用虚数的想法将这个圆进行变换 用MATLAB绘制一个坐标在(1,1)的圆代码如下 % 定义半径和圆心 radius 10; center 1 1i; % 圆心位于 (1,1) % 创建角度向量&#xff0c;从0到2*pi theta linspace(0, 2*pi, 100);% 计…

运算放大器组成的比较器

D1,D2为5.3V稳压管。运放输出高时&#xff0c;为6V.运放输入1V&#xff0c;运放正端为2V. 运放输出低时&#xff0c;为-6V&#xff0c;运放输入4V&#xff0c;运放正端约为2V&#xff0c;实测值2.03V

JavaScript 实现虚拟滚动技术

虚拟滚动 虚拟滚动&#xff08;有时称为 虚拟列表、虚拟滚动条&#xff09;是 JavaScript 中的一种技术&#xff0c;旨在优化大数据量的列表渲染&#xff0c;尤其是当有成千上万的数据项时&#xff0c;直接渲染整个列表会导致性能问题。虚拟列表通过只渲染用户视口中可见的那一…

【HuggingFace Transformers】OpenAIGPTModel源码解析

OpenAIGPTModel源码解析 1. GPT 介绍2. OpenAIGPTModel类 源码解析 说到ChatGPT&#xff0c;大家可能都使用过吧。2022年&#xff0c;ChatGPT的推出引发了广泛的关注和讨论。这款对话生成模型不仅具备了强大的语言理解和生成能力&#xff0c;还能进行非常自然的对话&#xff0c…

手机免费录屏软件,这3款软件最佳选择

在数字化浪潮的推动下&#xff0c;智能手机已成为我们生活中不可或缺的一部分。而在这些小巧而强大的设备中&#xff0c;录屏功能逐渐崭露头角&#xff0c;成为记录屏幕精彩瞬间的得力助手。无论是游戏的高光时刻、APP的使用教程&#xff0c;还是进行远程会议&#xff0c;录屏功…

2024自动化测试面试真题(附答案)!

一、编程语法题 1 、 python 有哪些数据类型 python 数据类型有很多&#xff0c;基本数据类型有整型&#xff08;数字&#xff09;、字符串、元组、列表、字典和布尔类型等 2 、怎么将两个字典合并 调用字典的 update 方法&#xff0c;合并 2 个字典。 3 、 json.l python 如…

HarmonyOS NEXT 体验调用云数据库更新排行榜单

一、介绍 基于鸿蒙Next模拟一个排行帮单二、场景需求 1.目标用户 社交平台用户&#xff0c;尤其是热衷于获取和分享信息的年轻人和用户群体。 2. 功能描述 用户可以通过“排行帮单”功能查看某个主题或领域的热门内容&#xff0c;并能够向朋友或群体推荐特定的项目。 3. 需求…