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

Swift 中的动态成员查找

请添加图片描述

在这里插入图片描述

文章目录

    • 前言
    • 基础介绍
    • 基础示例
      • 1. 定义一个动态成员访问类:
      • 2. 访问嵌套动态成员:
    • 使用 KeyPath 的编译时安全性
    • KeyPath 用法示例
    • KeyPath 进阶使用示例
      • 1. 动态访问属性:
      • 2. 结合可选属性和 KeyPath:
      • 3. 动态 KeyPath 和字典:
    • 总结

前言

在 Swift 中,动态成员查找是一种允许在编译时未知成员的情况下,通过字符串名称来访问属性和方法的机制。这在需要与动态数据进行交互,或者在某些情况下进行元编程时非常有用。动态成员查找通过 Swift 的 @dynamicMemberLookup 特性实现。

Glassfy:简化构建、管理和推广应用内购买。从订阅管理 SDK 到付费墙等完整的货币化工具。立即免费构建。

基础介绍

假设我们正在开发一个提供缓存功能的类型,并将其建模为名为 Cache 的结构体。

struct Cache {var storage: [String: Data] = [:]
}

为了访问缓存的数据,我们调用存储属性的下标,该存储属性是 Dictionary 类型提供的。

var cache = Cache()
let profile = cache.storage["profile"]

在这里没有什么特别之处。我们像以前一样通过 Dictionary 类型的下标访问字典。让我们看看如何使用 @dynamicMemberLookup 属性改进 Cache 类型的 API。

@dynamicMemberLookup
struct Cache {private var storage: [String: Data] = [:]subscript(dynamicMember key: String) -> Data? {storage[key]}
}

如上例所示,我们使用 @dynamicMemberLookup 属性标记了 Cache 类型。我们必须实现具有 dynamicMember 参数并返回我们需要的任何内容的下标。

var cache = Cache()
let profile = cache.profile

现在,我们可以更方便地访问 Cache 类型的配置文件数据。我们的 API 的使用者可能会认为配置文件是 Cache 类型的属性,但事实并非如此。

此特性完全在运行时工作,并利用了在点符号后键入的任何属性名称来访问 Cache 类型的下标,该下标具有 dynamicMember 参数。

整个逻辑在运行时运行,编译期间的结果是不确定的。在运行时,完全可以决定应该从下标返回哪些数据以及如何处理 dynamicMember 参数。

基础示例

以下是在 Swift 中使用动态成员查找的一些基本示例:

1. 定义一个动态成员访问类:

@dynamicMemberLookup
struct DynamicMemberExample {subscript(dynamicMember member: String) -> String {return "Accessed dynamic member: \(member)"}
}// 创建实例
let dynamicObject = DynamicMemberExample()// 使用动态成员查找
print(dynamicObject.someProperty) // 输出: Accessed dynamic member: someProperty
print(dynamicObject.someMethod()) // 输出: Accessed dynamic member: someMethod()

在上面的示例中,我们定义了一个结构体 DynamicMemberExample,使用 @dynamicMemberLookup 注解。这允许我们通过下标方法 subscript(dynamicMember:) 来实现对动态成员的访问。然后我们可以像使用普通成员一样使用动态成员。

2. 访问嵌套动态成员:

@dynamicMemberLookup
struct NestedDynamicMemberExample {struct Inner {subscript(dynamicMember member: String) -> String {return "Accessed dynamic member: \(member)"}}var inner = Inner()
}// 创建实例
let nestedDynamicObject = NestedDynamicMemberExample()// 使用动态成员查找
print(nestedDynamicObject.inner.someProperty) // 输出: Accessed dynamic member: someProperty

在这个示例中,我们在一个嵌套的结构体 Inner 中实现了动态成员查找。然后我们可以通过外部结构体 NestedDynamicMemberExample 的实例访问嵌套结构体的动态成员。

动态成员查找是一种强大的特性,但也要注意,它通常会损失一些类型安全性,因为在编译时不会检查动态成员的存在。使用动态成员查找时,请确保仔细考虑潜在的风险。

请注意,动态成员查找在 Swift 5.1 及更高版本中可用。如果在更早的版本中使用动态成员查找,可能需要进行适当的版本升级。

使用 KeyPath 的编译时安全性

我们唯一能找到的缺点是缺乏编译时安全性。我们可以将 Cache 类型视为代码中键入的任何属性名称。幸运的是,@dynamicMemberLookup 下标的参数不仅可以是 String 类型,还可以是 KeyPath 类型。

@dynamicMemberLookup
final class Store\<State, Action>: ObservableObject {
typealias ReduceFunction = (State, Action) -> State@Published private var state: Stateprivate let reduce: ReduceFunctioninit(initialState state: State,reduce: @escaping ReduceFunction) {self.state = stateself.reduce = reduce}subscript<T>(dynamicMember keyPath: KeyPath<State, T>) -> T {state[keyPath: keyPath]}func send(_ action: Action) {state = reduce(state, action)}}

如上例所示,我们定义了接受强类型 KeyPath 实例的 dynamicMember 参数下标。在这种情况下,我们允许 State 类型的 KeyPath,这有助于我们获得编译时安全性。因为每当我们传递与 State 类型无关的错误 KeyPath 时,编译器都会显示错误。

struct State {
var products: \[String] = \[]
var isLoading = false
}enum Action {
case fetch
}let store: Store\<State, Action> = .init(initialState: .init()) { state, action in
var state = state
switch action {
case .fetch:
state.isLoading = true
}
return state
}print(store.isLoading)
print(store.products)
print(store.favorites) // Compiler error

在上例中,我们通过接受 KeyPath 的下标访问 Store 的私有 state 属性。这看起来与前面的例子类似,但在这种情况下,只要尝试访问 State 类型的不可用属性,编译器就会显示错误。

KeyPath 用法示例

当涉及到 Swift 中的 KeyPath 时,实际的代码示例可以更好地阐明其概念和用法。下面将详细介绍先前提到的示例代码,以更清晰地展示 KeyPath 的用途和编译时安全性。

首先,我们定义一个名为 Person 的结构体,表示一个人的姓名和年龄:

struct Person {let name: Stringlet age: Int
}

接下来,我们创建一个 Person 实例,名为 person,并为其赋予姓名 “Alice” 和年龄 30:

let person = Person(name: "Alice", age: 30)

然后,我们使用 KeyPath 来引用 Person 实例的属性。我们创建了两个 KeyPath,一个用于访问姓名属性,另一个用于访问年龄属性:

let nameKeyPath = \Person.name
let ageKeyPath = \Person.age

使用 KeyPath,我们可以通过下标语法从 person 实例中访问属性值:

let personName = person[keyPath: nameKeyPath] // "Alice"
let personAge = person[keyPath: ageKeyPath]   // 30

在这里,我们通过 nameKeyPathageKeyPath 使用了 KeyPath,这是编译时类型安全的。如果我们尝试引用不存在的属性,编译器将在编译时捕获错误。

接下来,我们假设有一个数组 people 包含多个 Person 实例。我们可以使用 KeyPath 来对数组中的实例进行映射,以获取所有人的姓名和年龄:

let people = [Person(name: "Alice", age: 30),Person(name: "Bob", age: 25),Person(name: "Charlie", age: 28)
]let names = people.map(\.name) // ["Alice", "Bob", "Charlie"]
let ages = people.map(\.age)   // [30, 25, 28]

这里,我们使用 KeyPath 进行了数组的映射,从每个 Person 实例中提取了姓名和年龄。

最后,我们可以使用 KeyPath 对数组中的实例按年龄进行排序:

let sortedPeople = people.sorted(by: \.age)

通过传递 KeyPathsorted(by:) 方法,我们可以按年龄对人员进行排序。

总之,KeyPath 是 Swift 中的一项强大特性,它提供了类型安全的属性和下标访问方式,可以在编译时捕获错误,并具有映射、排序等便利功能。通过理解 KeyPath 的概念和用法,可以写出更安全、更易读和更高效的 Swift 代码。

KeyPath 进阶使用示例

下面介绍一下关于 KeyPath 更多的用法和示例

1. 动态访问属性:

let propertyName = "name"
let dynamicKeyPath = \Person[keyPath: propertyName] // Error: Cannot convert value of type 'String' to expected key path type 'WritableKeyPath<Person, _>'

在这个示例中,我们尝试使用字符串变量创建一个动态的 KeyPath。然而,这将会导致编译错误,因为编译器需要在编译时确定 KeyPath 的类型。

2. 结合可选属性和 KeyPath:

struct Team {let captain: Person?
}let team = Team(captain: Person(name: "David", age: 32))if let captainName = team.captain?[keyPath: \.name] {print("Captain's name: \(captainName)")
} else {print("No captain assigned")
}

在这个示例中,我们定义了一个 Team 结构体,其中的 captain 属性是可选的 Person 实例。通过使用 KeyPath,我们可以在安全的情况下访问可选属性。

3. 动态 KeyPath 和字典:

let personKeyPath: KeyPath<Person, String> = \.namelet personDictionary = ["name": "Ella", "age": "28"]
if let name = personDictionary[keyPath: personKeyPath] {print("Name from dictionary: \(name)")
} else {print("Name not found in dictionary")
}

在这个示例中,我们将 KeyPath 与字典结合使用。尽管字典中的键和 KeyPath 类型可能不匹配,但在运行时可以使用动态 KeyPath 访问字典中的值。

KeyPath 是 Swift 中非常有用的一项功能,它提供了类型安全的属性和下标访问方法,可以在编译时捕获错误,还支持映射、排序和与其他类型的组合使用。理解 KeyPath 的概念和用法将为编写更清晰、更安全的 Swift 代码提供强大的工具。

总结

KeyPath 是 Swift 编程语言中的一项强大功能,它为属性和下标提供了类型安全的访问方式,提升了代码的可读性、可维护性和性能。通过使用 KeyPath,我们可以避免使用字符串进行属性访问,从而减少了因拼写错误而引发的问题。

通过深入理解 KeyPath 的概念和用法,开发者可以编写更具类型安全性、可读性和性能的 Swift 代码。这一功能不仅简化了代码编写,还为我们提供了一种更加优雅的方式来处理属性和下标的访问与操作。

我们学习了如何使用 @dynamicMemberLookup 属性改进特定类型的 API。虽然并不是每个类型都需要它,但可以谨慎使用它来改善 API。

相关文章:

Swift 中的动态成员查找

文章目录 前言基础介绍基础示例1. 定义一个动态成员访问类&#xff1a;2. 访问嵌套动态成员&#xff1a; 使用 KeyPath 的编译时安全性KeyPath 用法示例KeyPath 进阶使用示例1. 动态访问属性&#xff1a;2. 结合可选属性和 KeyPath&#xff1a;3. 动态 KeyPath 和字典&#xff…...

leetcode做题笔记102. 二叉树的层序遍历

给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 思路一&#xff1a;递归 int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes){int** ans(int**)mal…...

python编写四画面同时播放swap视频

当代技术让我们能够创建各种有趣和实用的应用程序。在本篇博客中&#xff0c;我们将探索一个基于wxPython和OpenCV的四路视频播放器应用程序。这个应用程序可以同时播放四个视频文件&#xff0c;并将它们显示在一个GUI界面中。 C:\pythoncode\new\smetimeplaymp4.py 准备工作…...

用XSIBackup为VMware ESXi打造完美备份方案

文章目录 VMware ESXi 备份方案引言XSIBackup安装步骤1. XSIBackup软件安装2. SSH连接3. 定位到xsibackup目录4. 修改文件权限5. 安装cron查看crontab列表6. 配置备份任务结论VMware ESXi 备份方案 引言 数据就像是我们的生命线,一旦丢失,可能会带来无法挽回的损失。对于那…...

React 项目中引入msal验证以及部分报错处理

功能实现 如何在React 项目中引入msal身份验证&#xff0c; 微软在官网有提供文档支持&#xff0c;文档包含示例和具体使用的教程&#xff0c;地址如下&#xff1a; https://learn.microsoft.com/zh-cn/azure/active-directory/develop/tutorial-v2-nodejs-webapp-msal 照着文…...

Unity3D 2021 使用 SharpZipLib 遇到的安卓打包 I18N 相关问题

在 Unity3D 中&#xff0c;使用 ICSharpCode.SharpZipLib.dll 来做压缩和解压缩&#xff0c;但打包安卓后遇到问题&#xff0c;原因是字符编码程序集被裁减掉了导致。 根据网上搜索&#xff0c;将 UnityEditor 对应目录下的 I18N开头的&#xff0c;比如 I18N.CJK.dll 等系列文…...

软件工程(十五) 行为型设计模式(一)

1、责任链模式 简要说明 通过多个对象处理的请求,减少请求的发送者与接收者之间的耦合。将接受对象链接起来,在链中传递请求,直到有一个对象处理这个请求。 速记关键字 传递职责 类图如下 由类图可以比较容易的看出来,其实就是自己关联自己,形成了一个链,并且自己有…...

【校招VIP】前端算法考点之快慢指针题型

考点介绍&#xff1a; 链表是校招面试里手撕代码出现频度比较高的题型&#xff0c;三线和中小厂会考察简单的链表反转&#xff0c;大厂会进一步考察复杂度和双指针问题&#xff0c;比如中间元素、是否存在环等。 『前端算法考点之快慢指针题型』相关题目及解析内容可点击文章末…...

Docker基础入门:容器数据卷与Dockerfile构建镜像(发布)

Docker基础入门&#xff1a;容器数据卷与Dockerfile构建镜像&#xff08;发布&#xff09; 一、docker容器数据卷1.1、使用docker容器数据卷1.2、具名挂载、匿名挂载1.3、如何确定是具名挂载还是匿名挂载 二、使用dockerfile2.1 初识Dockerfile2.2 Dockerfile构建过程2.3 Docke…...

部署问题集合(二十一)从零开始搭建一台NAS服务器(Linux虚拟机)

前言 因工作需要&#xff0c;需要从零通过虚拟机搭建一台NAS服务器&#xff0c;以此记录下来 步骤 1、创建虚拟机 通过VMWare创建一台新虚拟机&#xff0c;虚拟机内存和磁盘自定义&#xff0c;不过建议尽量大一点 2、服务器端配置 查看是否安装有NFS服务&#xff1a;rpm …...

Git小白入门——了解分布式版本管理和安装

Git是什么&#xff1f; Git是目前世界上最先进的分布式版本控制系统&#xff08;没有之一&#xff09; 什么是版本控制系统&#xff1f; 程序员开发过程中&#xff0c;对于每次开发对各种文件的修改、增加、删除&#xff0c;达到预期阶段的一个快照就叫做一个版本。 如果有一…...

芯科科技宣布推出下一代暨第三代无线开发平台,打造更智能、更高效的物联网

第三代平台中的人工智能/机器学习引擎可将性能提升100倍以上 Simplicity Studio 6软件开发工具包通过新的开发环境将开发人员带向第三代平台 中国&#xff0c;北京 - 2023年8月22日 – 致力于以安全、智能无线连接技术&#xff0c;建立更互联世界的全球领导厂商Silicon Labs&…...

无涯教程-Android - Intents/Filters

Android Intent 是要执行的操作的抽象描述。它可以与 startActivity 一起启动Activity&#xff0c;将 broadcastIntent 发送给任何BroadcastReceiver组件&#xff0c;并与 startService(Intent)或 bindService(Intent&#xff0c;ServiceConnection&#xff0c;int)与后台服务进…...

NFTScan 正式上线 Base NFTScan 浏览器和 NFT API 数据服务

2023 年 8 月 24 号&#xff0c;NFTScan 团队正式对外发布了 Base NFTScan 基础设施&#xff0c;将为 Base 生态的 NFT 开发者和用户提供简洁高效的 NFT 数据搜索查询服务。NFTScan 作为全球领先的 NFT 数据基础设施服务商&#xff0c;Base 是继 Bitcoin、Ethereum、BNBChain、…...

【Git】测试持续集成——Git+Gitee+PyCharm

文章目录 概述一、使用Gitee1. 注册账号2. 绑定邮箱3. 新建仓库4. 查看项目地址 二、安装配置Git1. 下载安装包2. 校验是否安装成功。3. 配置Git4. Git命令5. Git实操 三、PyCharmGit1. 配置Git2. Clone项目3. 提交文件到服务器4. 从服务器拉取文件 概述 持续集成&#xff08;…...

《HelloGitHub》第 89 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …...

多维时序 | Matlab实现LSTM-Adaboost和LSTM多变量时间序列预测对比

多维时序 | Matlab实现LSTM-Adaboost和LSTM多变量时间序列预测对比 目录 多维时序 | Matlab实现LSTM-Adaboost和LSTM多变量时间序列预测对比预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | Matlab实现LSTM-Adaboost和LSTM多变量时间序列预测对比 模型…...

c语言每日一练(12)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…...

用AI + Milvus Cloud搭建着装搭配推荐系统

在上一篇文章中,我们学习了如何利用人工智能技术(例如开源 AI 向量数据库 Milvus Cloud 和 Hugging Face 模型)寻找与自己穿搭风格相似的明星。在这篇文章中,我们将进一步介绍如何通过对上篇文章中的项目代码稍作修改,获得更详细和准确的结果,文末附赠彩蛋。 注:试用此…...

41、springboot 整合 FreeMarker 模版技术

springboot 整合 FreeMarker 模版技术 ★ 整合FreeMarker的自动配置&#xff1a; FreeMarkerAutoConfiguration&#xff1a;负责整合Spring容器和获取FreeMarkerProperties加载的配置信息。FreeMarkerServletWebConfiguration/FreeMarkerReactiveWebConfiguration&#xff1a…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...