当前位置: 首页 > news >正文

Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出

在这里插入图片描述

0. 概览

Swift 5.9 一声炮响为我们带来全新的宏(Macro)机制,也同时带来了干霄凌云的 Observation 框架。

在这里插入图片描述

Observation 框架可以增强通用场景下的使用,也可以搭配 SwiftUI 5.0 而获得双剑合璧的更强威力。

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

  • 0. 概览
  • 1. @Observable 宏
  • 2. 通用情境下如何观察 Observable 对象?
  • 3. Observable 对象与 SwiftUI 珠联璧合
  • 4. 被“抛弃的” @EnvironmentObject
  • 5. 在视图中将不可变 Observable 对象转换为可变对象的妙招
  • 6. 总结

那么,就让我们赶快进入 Observation 奇妙的世界吧!

Let‘s go!!!😉


1. @Observable 宏

简单来说,Observation 框架为我们提供了集鲁棒性(robust)、安全性、高性能等三大特性为一身的 Swift 全新观察者设计模式。

它的核心功能在于:监视对象状态,并在改变时做出反应!

在这里插入图片描述

在 Swift 5.9 中,我们可以非常轻松的通过 @Observable 宏将普通类“转化为”可观察(Observable)类。自然,它们的实例都是可观察的:

@Observable
final class Hero {var name: Stringvar power: Intinit(name: String, power: Int) {self.name = nameself.power = power}
}@Observable
final class Model {var title: Stringvar createAt: Date?var heros: [Hero]init(title: String, heros: [Hero]) {self.title = titleself.createAt = Date.nowself.heros = heros}
}

如上代码所示,我们定义了两个可观察类 Model 和 Hero,就是这么简单!

2. 通用情境下如何观察 Observable 对象?

在一个对象成为可观察之后,我们可以通过 withObservationTracking() 方法随时监听它状态的改变:

在这里插入图片描述

我们可以将对象需要监听的属性放在 withObservationTracking() 的 apply 闭包中,当且仅当( Hero 中其它属性的改变不予理会)这些属性发生改变时其 onChange 闭包将会被调用:

let hero = Hero(name: "大熊猫侯佩", power: 5)func watching() {withObservationTracking({NSLog("力量参考值:\(hero.power)")}, onChange: {NSLog("改变之前的力量!:\(hero.power)")watching()})
}watching()hero.name = "地球熊猫"
hero.power = 11
hero.power = 121

以上代码输出如下:

在这里插入图片描述

使用 withObservationTracking() 方法有 3 点需要注意:

  1. 它默认只会被调用 1 次,所以上面为了能够重复监听,我们在 onChange 闭包里对 watching() 方法再次进行了调用;
  2. withObservationTracking() 方法的 apply 闭包不管如何都会被调用 1 次,即使其监听的属性从未改变过;
  3. 在监听闭包中只能得到属性改变前的旧值;

目前,上面测试代码在 Xcode 15 的 Playground 中编译会报错,提示如下:

error: test15.playground:8:13: error: external macro implementation type ‘ObservationMacros.ObservableMacro’ could not be found for macro ‘Observable()’
final class Hero {
^

Observation.Observable:2:180: note: ‘Observable()’ declared here
@attached(member, names: named(_$observationRegistrar), named(access), named(withMutation)) @attached(memberAttribute) @attached(extension, conformances: Observable) public macro Observable() = #externalMacro(module: “ObservationMacros”, type: “ObservableMacro”)

小伙伴们可以把它们放在 Xcode 的 Command Line Tool 项目中进行测试:

在这里插入图片描述


3. Observable 对象与 SwiftUI 珠联璧合

要想发挥 Observable 对象的最大威力,我们需要 SwiftUI 来一拍即合。

在 SwiftUI 中,我们无需再显式调用 withObservationTracking() 方法来监听改变,如虎添翼的 SwiftUI 已为我们自动完成了所有这一切!

struct ContentView: View {let model = Model(title: "地球超级英雄", heros: [])var body: some View {        NavigationStack {Form {LabeledContent(content: {Text(model.title)}, label: {Text("藏匿点名称")})LabeledContent(content: {Text(model.createAt?.formatted(date: .omitted, time: .standard) ?? "无")}, label: {Text("更新时间")})Button("刷新") {// SwiftUI 会自动监听可观察对象的改变,并刷新界面model.title = "爱丽丝仙境兔子洞"model.createAt = Date.now}}.navigationTitle(model.title)}}
}

注意,上面代码中 model 属性只是一个普通的 let 常量,即便如此 model 的改变仍会反映到界面上:

在这里插入图片描述

4. 被“抛弃的” @EnvironmentObject

有了 Swift 5.9 中新 Observation 框架加入游戏,在 SwiftUI 5.0 中 EnvironmentObject 再无用武之地,我们仅用 Environment 即可搞定一切!

早在 SwiftUI 1.0 版本时,其就已经提供了 Environment 对应的构造器:

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen @propertyWrapper public struct Environment<Value> : DynamicProperty {...}

有了新 Observation 框架的入驻,结合其 Observable 可观察对象,Environment 可以再次大放异彩:

struct HeroListView: View {@Environment(Model.self) var modelvar body: some View {List(model.heros) { hero inHStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}}
}struct ContentView: View {@State var model = Model(title: "地球超级英雄", heros: [.init(name: "大熊猫侯佩", power: 5),.init(name: "孙悟空", power: 1000),.init(name: "哪吒", power: 511)])var body: some View {        NavigationStack {Form {NavigationLink(destination: HeroListView().environment(model)) {Text("查看所有英雄")}}.navigationTitle(model.title)}}
}

现在,即使跨越多重层级关系我们也可以只通过 @Environment 而不用 @EnvironmentObject 来完成状态的间接传递了,是不是很赞呢?👍🏻

5. 在视图中将不可变 Observable 对象转换为可变对象的妙招

介绍了以上这许多,就还剩一个主题没有涉及:**Observable 对象的可变性!
**

为了能够在子视图中更改对应的可观察对象,我们可以用 @Bindable 修饰传入的 Observable 对象:

struct HeroView: View {@Bindable var hero: Herovar body: some View {Form {TextField("名称", text: $hero.name)TextField("力量", text: .init(get: {String(hero.power) }, set: {hero.power = Int($0) ?? 0}))}}
}

不过,对于之前 @Environment 那个例子来说,如何达到子视图能够修改传入的 @Environment 可观察对象呢?

别急,我们可以利用称为“临时可变(Temporary Variable)”的技术将原先不可变的可观察对象改为可变:

extension Hero: Identifiable {var id: String {name}
}struct HeroListView: View {@Environment(Model.self) var modelvar body: some View {// 在 body 内将 model 改为可变@Bindable var model = modelVStack {List(model.heros) { hero inHStack {Text(hero.name).font(.headline)Spacer()Text("\(hero.power)").font(.subheadline).foregroundStyle(.gray)}}.safeAreaInset(edge: .bottom) {// 绑定可变 model 中的状态以修改英雄名称TextField("", text: $model.heros[0].name).padding()} }}
}

运行效果如下:

在这里插入图片描述

“临时可变”这一技术可以用于视图中任何化“不变”为“可变”的场景中,当然对于直接视图间对象的传递,我们可以使用 @Bindable 这一更为“正统”的方法。

6. 总结

在本篇博文中,我们讨论了在 Swift 5.0 和 SwiftUI 5.0 中大放异彩 Observation 框架的使用,并就诸多技术细节问题给与了详细的介绍,愿君喜欢。

感谢观赏,再会!😎

相关文章:

Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出

0. 概览 Swift 5.9 一声炮响为我们带来全新的宏&#xff08;Macro&#xff09;机制&#xff0c;也同时带来了干霄凌云的 Observation 框架。 Observation 框架可以增强通用场景下的使用&#xff0c;也可以搭配 SwiftUI 5.0 而获得双剑合璧的更强威力。 在本篇博文&#xff0c…...

【已解决】在 Vite 项目中使用 eslint-config-ali 时遇到的解析错误

错误还原 搭建 Vite 项目 pnpm create vite my-vue-app --template vue-ts安装 eslint-config-ali pnpm i -D eslint-config-ali typescript-eslint/parser typescript-eslint/eslint-plugin eslint-plugin-import eslint-import-resolver-typescript vue-eslint-parser esl…...

蓝桥杯每日一题2023.10.5

3420. 括号序列 - AcWing题库 题目描述 题目分析 对于这一我们需要有前缀知识完全背包 完全背包的朴素写法&#xff1a; #include<bits/stdc.h> using namespace std; const int N 1010; int n, m, v[N], w[N], f[N][N]; int main() {cin >> n >> m;fo…...

PyTorch实例:简单线性回归的训练和反向传播解析

文章目录 &#x1f966;引言&#x1f966;什么是反向传播&#xff1f;&#x1f966;反向传播的实现&#xff08;代码&#xff09;&#x1f966;反向传播在深度学习中的应用&#x1f966;链式求导法则&#x1f966;总结 &#x1f966;引言 在神经网络中&#xff0c;反向传播算法…...

Arcgis提取玉米种植地分布,并以此为掩膜提取遥感影像

Arcgis提取玉米种植地分布上&#xff0c;并以此为掩膜提取遥感影像 一、问题描述 因为之前反演是整个研究区&#xff0c;然而土地利用类型有很多类&#xff0c;只在农田或者植被上进行反演&#xff0c;需要去除水体、建筑等其他类型&#xff0c;如何处理得到下图中只有耕地类…...

软件工程与计算总结(四)项目管理基础

目录 一.项目和项目管理 二.团队组织与管理 三.软件质量保障 四.软件配置管理 五.项目实践 一.项目和项目管理 1.软件开发远不是纯粹的编程&#xff0c;随着软件规模的增长&#xff0c;软件开发活动也变得越来越复杂~ 2.软件项目就是要将所有的软件开发活动组织起来&#…...

【Python】datetime 库

# timedelta(days, seconds, microseconds,milliseconds, minutes, hours, weeks) 默认按顺序传递参数 # 主要介绍 datetime.datetime 类 # 引入 from datetime import datetime today datetime.now() # 获取当前时间 2023-10-05 15:58:03.218651 today1 datetime.utcnow() #…...

从0开始python学习-28.selenium 需要图片验证的登录

url https://test.com/login driver.get(url) # 获取登录页面需要输入账号密码进行模拟登录操作 user driver.find_element(By.XPATH,//*[id"login"]/div[2]/div/form[2]/div[2]/div/div/input).send_keys(username) pwd driver.find_element(By.XPATH,//*[id&qu…...

Nginx搭建Rtmp流媒体服务,并使用Ffmpeg推流

文章目录 1.rtmp流媒体服务框架图2.nginx配置3.配置nginx4.使用ffmpeg推流5.实时推摄像头流 本项目在开发板上使用nginx搭建流媒体服务&#xff0c;利用ffmpeg进行推流&#xff0c;在pc上使用vlc media进行拉流播放。 1.rtmp流媒体服务框架图 2.nginx配置 下载&#xff1a;wge…...

IDEA 将一个普通Java工程转化为maven工程

打开IntelliJ IDEA并打开Java工程。 在项目窗口中&#xff0c;右键单击项目名称&#xff0c;选择“Add Framework Support”。 在弹出的窗口中&#xff0c;选择“Maven”。 在“Maven Information”窗口中&#xff0c;填写Group Id、Artifact Id和Version等基本信息。 点击…...

linux下的永久保存行号

linux下的永久保存行号 1.首先 这里是引用 输入命令&#xff1a;vi ~/.vimrc 其次 这里是引用 输入命令 set number...

92岁高龄的创始人张忠谋谈台积电发展史

一、张忠谋和台积电 在台北一间办公室里&#xff0c;张忠谋最近拿出一本印有彩色图案的旧书。它的标题是《VLSI 系统导论》&#xff0c;这是一本研究生水平的教科书&#xff0c;描述了计算机芯片设计的复杂性。92岁的张先生满怀敬意地举起它。 92岁高龄的台积电创始人张忠谋 “…...

【VIM】VIm初步使用

玩转Vim-从放弃到入门_哔哩哔哩_bilibili...

教育类《中学政史地》收稿方向-投稿邮箱

教育类《中学政史地》收稿方向-投稿邮箱 《中学政史地》收稿方向&#xff1a;中学政治、历史、地理类稿件 《中学政史地》创办于1987年&#xff0c;是我国唯一一份集中学政治、历史、地理三门学科为一体的综合性月刊。每月两期&#xff0c;分初中版和高中版。以服务学生、服务…...

数据库的备份与恢复

数据备份的重要性 备份的主要目的是灾难恢复。 在生产环境中&#xff0c;数据的安全性至关重要。 任何数据的丢失都可能产生严重的后果。 造成数据丢失的原因&#xff1a; 程序错误人为操作错误运算错误磁盘故障灾难&#xff08;如火灾、地震&#xff09;和盗窃 数据库备份…...

string类的模拟实现(万字讲解超详细)

目录 前言 1.命名空间的使用 2.string的成员变量 3.构造函数 4.析构函数 5.拷贝构造 5.1 swap交换函数的实现 6.赋值运算符重载 7.迭代器部分 8.数据容量控制 8.1 size和capacity 8.2 empty 9.数据修改部分 9.1 push_back 9.2 append添加字符串 9.3 运算符重载…...

C 函数指针

就像指针可以指向一般变量、数组、结构体那样&#xff0c;指针也可以指向函数。 函数指针的主要用途是向其他函数传递“回调”&#xff0c;或者模拟类和对象。 形式如下&#xff1a; int (*POINTER_NAME)(int a, int b) 这类似于指向数组的指针可以表示所指向的数组。指向函数…...

zkVM设计性能分析

1. 引言 本文主要参考&#xff1a; 2023年9月ZKSummit10 Wei Dai 1k(x) & Terry Chung 1k(x)分享视频 ZK10: Analysis of zkVM Designs - Wei Dai & Terry Chung 当前有各种zkVM&#xff0c;其设计思想各有不同&#xff0c;且各有取舍&#xff0c;本文重点对现有各z…...

调用gethostbyname实现域名解析(附源码)

VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#xff09;https://blog.csdn.net/chenlycly/article/details/124272585C软件异常排查从入门到精通系列教程&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&a…...

面向无线传感器网络WSN的增强型MODLEACH设计与仿真(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG

TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码&#xff1a;HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...