Swift 中强大的 Key Paths(键路径)机制趣谈(上)

news2024/10/5 17:21:34

在这里插入图片描述

概览

小伙伴们可能不知道:在 Swift 语言中隐藏着大量看似“其貌不扬”实则却让秃头码农们“高世骇俗”,堪称卧虎藏龙的各种秘技。

在这里插入图片描述

其中,有一枚“不起眼”的小家伙称之为键路径(Key Paths)。如若将其善加利用,必将在实际撸码中大放异彩,如虎添翼!

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

  • 概览
  • 1. 一窥门径:键路径(Key Paths)初步
  • 2. 功能快速简化之妙用
  • 3. 将键路径当做方法传递
  • 总结

本篇和下一篇皆为看似“平淡无奇”的键路径“凤凰涅槃”、逆袭重生的“励志”博文!到底如何?且看分解!

闲言少叙,Let’s change our destiny against the heavens!!!😉


1. 一窥门径:键路径(Key Paths)初步

我们知道 Swift 语言最初的设计重点是编译时安全和静态类型。因此,它势必会缺乏那些更加关注运行时语言(如 Objective-C、Ruby 和 JavaScript)中常见的那种动态特性。例如,在 Objective-C 中,我们可以在运行时动态访问对象中的任何属性和方法,甚至交换、修改其相关的实现。


想要了解更多 Swift 动态机制内容的小伙伴们,请移步如下链接观赏精彩的内容:

  • 『番外篇二』Swift “黑魔法”之动态获取类实例隐藏属性的值
  • SwiftUI 利用 Swizz 黑魔法为系统创建的默认对象插入新协议方法(五)
  • SwiftUI 利用 Swizz 黑魔法为系统创建的默认对象插入新协议方法(六)
  • SwiftUI 如何在运行时从底层动态获取任何 NSObject 对象实例
  • 『番外篇五』SwiftUI 进阶之如何动态获取任意视图的 tag 和 id 值

虽然这种缺乏动态性的特点可能恰恰是 Swift 如此强大的主要缘由,因为它可以帮助我们编写可预测性更强的逻辑,并且有更大的概率撸出“正确”的代码。

不过,有时能够以更动态的方式处理我们的实现夙愿也会非常有用。

谢天谢地!进化中的 Swift 语言不断汲取着越来越多本质上更动态的功能,同时也仍然专注于类型的安全性,这其中一个不可或缺的特性便是键路径(Key Paths)。

在这里插入图片描述

在 Swift 中广义的键路径是指一种动态访问和修改对象属性的机制,而狭义的键路径则用来表示特定根类型上特定属性值的类型。

在这里插入图片描述

一般来说存在三种键路径:

  1. KeyPath: 最常见的形式,用来提供到某一类型特定属性的只读路径;
  2. WritableKeyPath: 用值语义(value semantics)提供到某一类型特定属性的读写路径(因而,该类型的实例也必须是可写的);
  3. ReferenceWritableKeyPath: 和 WritableKeyPath 类似,不过只能用在引用类型上(比如类);

除了上面最常见的三种键路径类型以外,还有其它一些键路径。它们主要被用于减少内部代码重复或帮助类型抹除(Type erase)等情况,限于篇幅就不在本文中介绍了,

如果想要进一步了解这些额外键路径类型,请小伙伴们移步如下链接观赏进一步的内容:

  • Key-Path Expressions
  • KeyPath Documentation

在初步了解键路径的基本概念之后,下面就让我们深入探寻一番如何使用关键路径,以及它们因何而有趣、又因何而强大吧。

2. 功能快速简化之妙用

假设我们正在构建一款应用程序,它允许用户阅读来自网页的内容。我们设计了一个 Article 模型用来表示 Web 页面中对应的文章,如下所示:

struct Article {
    let id: UUID
    let source: URL
    let title: String
    let body: String
}

在大多数情况下,每当我们使用这样的模型数组时,通常希望从数组每个元素中提取一块数据以形成新的数组 —— 例如,在下面两个示例中我们从一组文章(Article)中收集了所有的 id 和 source:

let articleIDs = articles.map { $0.id }
let articleSources = articles.map { $0.source }

虽然上面的代码并没有什么错,不过由于我们的愿望只是单纯地从数组元素的单个属性中提取值,所以使用闭包看似有些大材小用了。

在这里,换为键路径会更加恰如其分。

extension Sequence {
    func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
        return map { $0[keyPath: keyPath] }
    }
}

如上代码所示,我们为序列(Sequence)增加了一个协议扩展方法 map,该方法的参数为序列元素任意属性的键路径。我们在 map 方法的实现中巧妙利用 Swift 下标( subscript)语法糖“有胆有识”的访问了序列元素属性的值。

这样一来,我们即可以用非常 Nice 的语法来提取序列元素任意属性的内容啦!所以之前的代码可以重构为如下形式了:

let articleIDs = articles.map(\.id)
let articleSources = articles.map(\.source)

虽然这让秃头码农们觉得很酷,不过键路径真正熠熠生辉的地方是当它们用于构建更复杂表达式的时候:比如在对数组排序时。

众所周知,Swift 标准库能够自动对包含 Sortable 元素的任何序列进行排序,但对于所有其它类型,我们必须提供自己的排序闭包。然而,使用键路径我们也可以轻而易举地添加对基于可比较键路径序列元素进行排序的支持。

与之前类似,我们将在 Sequence 协议上添加一个扩展方法,它的作用是将给定的键路径转换为排序表达式闭包:

extension Sequence {
    func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
        return sorted { a, b in
            return a[keyPath: keyPath] < b[keyPath: keyPath]
        }
    }
}

有了上面的“铺垫”,我们现在只需给出想要排序的键路径,即可优哉游哉地对任何类型元素的序列进行排序啦。

假若我们正在构建的 App 需要处理任何形式的可排序列表,例如包含播放列表的音乐应用程序 —— 这将非常方便,因为我们现在可以根据任何可比较属性(甚至嵌套属性)对列表进行排序了:

playlist.songs.sorted(by: \.name)
playlist.songs.sorted(by: \.dateAdded)
playlist.songs.sorted(by: \.ratings.worldWide)

上面代码看起来就像在优雅地添加“甜美的”语法糖,这既可以使处理序列复杂的代码更容易阅读,也可以帮助减少代码重复(DRY):因为小伙伴们现在可以“为所欲为”的向任何属性重用相同的排序逻辑啦,很棒哦!

3. 将键路径当做方法传递

一个好消息是:从 Swift 5.2 开始,上面 Sequence 扩展中的 map 方法已不再需要,因为键路径如今可以自动地转换为方法啦(converted into functions)!

这可能只是 Swift 语言进化中的一小步,但却是键路径“功成名就”的一大步!因为它会使序列上功能闭包的调用看起来更加“青出于蓝” —— 因为我们现在可以直接传递该属性的键路径了:

struct Movie {
    var name: String
    var isFavorite: Bool
    ...
}

let movies: [Movie] = loadMovies()

// 等价于 movies.map { $0.name }
let movieNames = movies.map(\.name)

// 等价于 movies.filter { $0.isFavorite }
let favoriteMovies = movies.filter(\.isFavorite)

总结

在本篇博文中,我们先是介绍了 Swift 语言中“简约却不简单”的键路径(Key Paths)机制,接着讨论了将它用来简化逻辑以及当成方法(functions)传递的美妙瞬间。

我们将在下一篇博文中继续介绍如何用键路径超越对象实例,特例化(specialize)数据模型;以及用可写键路径彻底摆脱“引用循环”,让简化代码“一蹴而就”。

感谢观赏,下一篇再会喽!😎

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

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

相关文章

STM32 中断编程入门

目录 一、中断系统 1、中断的原理 2、中断类型 外部中断 定时器中断 DMA中断 3、中断处理函数 中断标志位清除 中断服务程序退出 二、实际应用 中断控制LED 任务要求 代码示例 中断控制串口通信 任务要求1 代码示例 任务要求2 代码示例 总结 学习目标&…

什么是文档透明加密|好用的文档透明加密软件有哪些?

在当今日益数字化和信息化的时代&#xff0c;数据安全问题愈发受到企业和个人的关注。文档作为信息的重要载体&#xff0c;其安全性不言而喻。为了保障文档的机密性和完整性&#xff0c;文档透明加密技术应运而生。本文将对文档透明加密进行详细介绍&#xff0c;并探讨一些好用…

6月份上海二手房卖疯了,暴涨四成,反价房东被抛弃

6月份刚刚结束&#xff0c;北京、上海两大城市的房市成交情况纷纷出炉&#xff0c;从成交量来看上海房市明显比北京火热许多&#xff0c;同时与其他城市类似&#xff0c;消费者偏向于二手房。 6月份上海二手房往前高达2.6万套&#xff0c;环比增加超四成&#xff0c;创下2021年…

粤港联动,北斗高质量国际化发展的重要机遇

今年是香港回归27周年&#xff0c;也是《粤港澳大湾区发展规划纲要》公布5周年&#xff0c;5年来各项政策、平台不断为粤港联动增添新动能。“十四五”时期的粤港澳大湾区&#xff0c;被国家赋予了更重大的使命&#xff0c;国家“十四五”《规划纲要》提出&#xff0c;以京津冀…

用MySQL+node+vue做一个学生信息管理系统(二):创建MySQL数据表、创建HTML用户列表页面

MySQL代码 CREATE DATABASE students;USE students;CREATE TABLE student( id INT COMMENT 学号, name VARCHAR(32) COMMENT 姓名, sex VARCHAR(8) COMMENT 性别, class VARCHAR(64) COMMENT 班级 )SHOW TABLES;下面介绍一下Vue框架的element-ui的使用方法&#xff0c;这里就不…

【哈尔滨二级等保测评需要测哪些指标】

为了保证系统的安全性、稳定性和遵从性&#xff0c;哈尔滨二级等保评估要求对评估指标进行全面的评估。下面就是对哈尔滨等保二级考核所需要的考核指标的具体说明&#xff0c;并按各个维度分点表达与总结&#xff1a; 一、物理安全要求 物理安全是信息系统的根本&#xff0c;…

go-zero微服务教程(一、基本介绍与安装)

系列文章目录 第一章 go-zero基本介绍与安装 文章目录 一、基本介绍1.1 参考1.2 什么是微服务1.3 什么是go-zero1.4 为什么选择go-zero1.5 go-zero的整体架构1.6 go-zero的特点 二、安装2.1 go-zero需要安装的组件2.2 安装 protoc 及代码生成插件2.3 安装用于生成 go 和 grpc 相…

工业触摸一体机优化MES应用开发流程

工业触摸一体机在现代工业生产中扮演着至关重要的角色&#xff0c;它集成了智能触摸屏和工业计算机的功能&#xff0c;广泛应用于各种生产场景中。而制造执行系统&#xff08;MES&#xff09;作为工业生产管理的重要工具&#xff0c;对于提高生产效率、降低成本、优化资源利用具…

XTDrone-固定翼无人机编队跟踪无人车-配置教程

配置使用ROS版本为Neotic 1 配置 1.1 加载固定翼无人机编队跟踪控制工程文件 cp -r ~/XTDrone/coordination/fixed_wing_formation_control ~/catkin_ws/src 1.2 加载一些用到的功能包 sudo apt-get install ros-noetic-serial #根据自己的ROS版本修改 sudo apt-get insta…

[图解]SysML和EA建模住宅安全系统-07-to be块定义图

1 00:00:00,180 --> 00:00:06,820 我们来看&#xff0c;这是之前的那张图&#xff0c;现有的 2 00:00:08,290 --> 00:00:09,160 我们怎么做 3 00:00:09,170 --> 00:00:11,280 你看&#xff0c;我们之前 4 00:00:11,290 --> 00:00:15,600 在现状&#xff0c;as i…

lstrip()方法——截掉字符串左边的空格或指定的字符

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 lstrip()方法用于截掉字符串左边的空格或指定的字符。lstrip()方法的语法格式如下&#xff1a; str.lstrip([chars]) 参数说明&#xff…

多语言模型(Multilingual Models)用于推理(Inference)

在深入探讨多语言模型&#xff08;Multilingual Models&#xff09;用于推理&#xff08;Inference&#xff09;的详细内容时&#xff0c;我们需要首先理解多语言模型的基本概念、它们如何工作、为什么它们在现代自然语言处理&#xff08;NLP&#xff09;中变得如此重要&#x…

中小企业如何防止被查盗

在当前的商业环境中&#xff0c;小企业面临诸多挑战&#xff0c;其中之一便是如何在有限的预算内满足日常运营的技术需求。由于正版软件的高昂成本&#xff0c;一些小企业可能会选择使用盗版软件来降低成本。 我们联网之后存在很多风险&#xff0c;你可以打开自己的可以联网的电…

TOGAF培训什么内容?参加TOGAF培训有什么好处?考试通过率多少?

TOGAF培训什么内容&#xff1f;参加TOGAF培训有什么好处&#xff1f;考试通过率多少&#xff1f; TOGAF培训哪些内容&#xff1f; 通过本课程&#xff0c;你将掌握TOGAF的理论和实践&#xff0c;理解企业架构的影响&#xff0c;能够评估、启动、设 计、执行新一轮企业和IT架构…

Librechat快速部署指南

引言 Github的开源免费程序里&#xff0c;Librechat作为AI对话使用&#xff0c;现阶段可谓是最佳选择&#xff0c;配合聚合API >>进行使用&#xff0c;能够保证成本最低&#xff0c;自由度最高&#xff0c;私密性最强&#xff0c;功能丰富且界面美观&#xff0c;如此以来…

老旧芯片糊弄不了国人,国产手机纷纷降价,直逼千元

国产手机今年特别糊弄国内消费者&#xff0c;将2022年乃至2018年的芯片稍微升级&#xff0c;或者就是直接改名重新上市&#xff0c;国产手机以为他们可以凭借市场份额优势迫使消费者购买这些采用落后芯片的手机&#xff0c;然而现实说明他们错了。 近期几家国产手机品牌纷纷将这…

生物分子生物学实验过程的自动化与智能监控系统设计

开题报告&#xff1a;生物分子生物学实验过程的自动化与智能监控系统设计 一、引言 随着生物科学技术的飞速发展&#xff0c;生物分子生物学实验在科研、医疗、农业等领域的应用日益广泛。然而&#xff0c;传统的生物分子生物学实验过程大多依赖于人工操作&#xff0c;存在操…

网安加·百家讲坛 | 刘志诚:从安全(Safety)团队看OpenAI之争的本质

作者简介&#xff1a;刘志诚&#xff0c;乐信集团信息安全中心总监、OWASP广东区域负责人、网安加社区特聘专家。专注于企业数字化过程中网络空间安全风险治理&#xff0c;对大数据、人工智能、区块链等新技术在金融风险治理领域的应用&#xff0c;以及新技术带来的技术风险治理…

【UML用户指南】-26-对高级行为建模-状态图

目录 1、概念 2、组成结构 3、一般用法 4、常用建模技术 4.1、对反应型对象建模 一个状态图显示了一个状态机。在为对象的生命期建模中 活动图展示的是跨过不同的对象从活动到活动的控制流 状态图展示的是单个对象内从状态到状态的控制流。 在UML中&#xff0c;用状态图…

基于MATLAB对线阵天线进行泰勒加权

相控阵天线——基于MATLAB对线阵进行泰勒加权 目录 前言 一、泰勒综合 二、单元间距的改变对泰勒阵列方向图的影响 三、单元数的改变对泰勒阵列激励分布的影响 四、副瓣电平SLL对泰勒阵列激励幅度的影响 五、副瓣电平SLL对泰勒阵列方向图的影响 六、泰勒阵列和切比雪夫阵…