SwiftUI 趣谈之:绝不可能(Never)的 View!
概览
SwiftUI 的出现极大的解放了秃头码农们的生产力。SwiftUI 中众多原生和自定义视图对于我们创建精彩撩人的 App 功不可没!
不过,倘若小伙伴们略微留意过 SwiftUI 框架头文件里的源代码,就会发现里面嵌有一些奇怪 Never 类型,带来阵阵“违和感”:
那么 Never 到底是一种怎样的存在?它们在 SwiftUI 中又到底扮演着什么角色呢?
在本篇博文中,您将学到如下内容:
- 概览
- 1. 莫名其妙的”Never“!?
- 2. 什么!Never 竟然是一种”视图“?
- 3. Never 在 SwiftUI 视图中的作用
- 4. 尝试创建一个自定义原生 “Never” SwiftUI 视图
- 总结
闲言少叙,Let‘s find out!!!😉
1. 莫名其妙的”Never“!?
各位小伙伴们可能会奇怪 Never 到底表示什么?如果没记错的话,Never 的定义早在 SwiftUI 之前就已是 Swift(3.0)里的“囊中之物”了:
从 官方代码的注释中可以清楚的看到 Never 存在的意义:
/// The return type of functions that do not return normally, that is, a type
/// with no values.
///
/// Use `Never` as the return type when declaring a closure, function, or
/// method that unconditionally throws an error, traps, or otherwise does
/// not terminate.
///
/// func crashAndBurn() -> Never {
/// fatalError("Something very, very bad happened")
/// }
由上可知,Never 在 Swift 中主要有两种用途:
- 表示非正常返回方法(或函数、闭包)的返回类型(比如抛出异常、断言等);
- 表示没有值的类型;
比如,虽然和实际返回类型不一致,下面的 test 和 otherTest 方法都在某些错误条件下“返回” 了 Never 值:
func test(a: Int, b: Int) -> Int {guard b != 0 else { fatalError()}return a/b
}func otherTest(items: [String]) -> [String] {guard !items.isEmpty else { preconditionFailure("不能为空!") }return items.map { $0.debugDescription }
}test(a: 10, b: 0)
otherTest(items: [])
通过查看 fatalError() 和 preconditionFailure() 函数的定义,我们发现它们哥俩都会返回 Never:
public func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Neverpublic func preconditionFailure(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never
同样,Never 也可以用来表示某种类型“不存在”的值(注意这种不存在和 nil 并不相同):
let p = PassthroughSubject<Int,Never>()
如上代码所示,我们定义的 PassthroughSubject 发布器永远不会发生错误(其错误类型为 Never)!
经过上面的讨论我们可以发现,Never 的作用比想象的要大的多!
值得注意的是,作为枚举类型的 Never 不能被实例化(至少我们从外部不能),我们只能“享用”它们现成的实例。
那么,Never 和 SwiftUI 又有怎样的关系呢?
2. 什么!Never 竟然是一种”视图“?
是滴,你没看错,Never 在 SwiftUI 中做了扩展,它确实可以表示为一种“视图”类型:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never : View {
}@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never {/// The type for the internal content of this `AccessibilityRotorContent`.public typealias Body = Never/// The internal content of this `AccessibilityRotorContent`.public var body: Never { get }
}
比如,我们耳熟能详的 VStack 定义中就有 Never 可爱的身影:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
@frozen public struct VStack<Content> : View where Content : View {@inlinable public init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, @ViewBuilder content: () -> Content)public typealias Body = Never
}
而且,Never 在 SwiftUI 不仅是一种视图,它还可以是一种 ShapeStyle、
TableColumnContent、Gesture 甚至一种 Scene:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never : ShapeStyle {/// The type of shape style this will resolve to.////// When you create a custom shape style, Swift infers this type/// from your implementation of the required `resolve` function.public typealias Resolved = Never
}@available(iOS 16.0, macOS 12.0, *)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
extension Never : TableColumnContent {/// The type of sort comparator associated with this table column content.public typealias TableColumnSortComparator = Never/// The type of content representing the body of this table column content.public typealias TableColumnBody = Never/// The composition of content that comprise the table column content.public var tableColumnBody: Never { get }
}@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never : Gesture {/// The type representing the gesture's value.public typealias Value = Never
}@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension Never : Scene {
}
看到这里,小伙伴呢可能感觉有些“头晕目眩”。SwiftUI 里搞这么多 Never 到底是要闹哪样呢?
3. Never 在 SwiftUI 视图中的作用
虽然很多小伙伴们都早已对 SwiftUI 撸码驾轻就熟,不过大家有没有考虑过这样一个问题:我们知道每个 View 都有一个 Body(类型为 some View),“爷爷”视图的 Body 是“爸爸”视图,而“爸爸”视图的 Body 是“儿子”视图…这样下去会出现“子子孙孙无穷尽”的情况,最终总要有一个最后的视图啊!
比如,观察下面的代码:
struct Text: View {var body: some View {???}
}struct Son: View {var body: some View {Text("Son")}
}struct Baba: View {var body: some View {Son()}
}
其中,Baba 的 Body 中是 Son 视图,而 Son 的 Body 嵌入的是 Text 视图,那么 Text 里面又该怎么实现呢?我们假设 Text 里面还有一个 InnerText 视图,那么 InnterText 里又该如何?
这就是 Never 在 SwiftUI 中存在的绝佳意义:它终结了上面这种无穷尽的视图 Body 链!
那么,视图嵌套到底在哪里终结呢?答案就是在 SwiftUI 内置的原生视图里。
比如在上面的例子中,最终 Son 中里面是一个 Text 视图,大家都知道 Text 视图是 SwiftUI 提供的众多原生视图之一。从码农的角度来看它不能再被分解,从某种意义上可以认为它是一个“原子”视图:
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text : View {/// The type of view representing the body of this view.////// When you create a custom view, Swift infers this type from your/// implementation of the required ``View/body-swift.property`` property.public typealias Body = Never
}
看到了吗?SwiftUI 框架头文件里将 Text 的 Body 类型定义为了 Never,正是这一个小小的 Never 将我们从“无穷无尽”中解脱出来,整个世界变得清净了…
综上所述,SwiftUI 不可能永远询问嵌套视图的 Body,它需要特殊的视图,比如那些“原子”视图作为“终结者”,这样 SwiftUI 就可以停止“刨根问底”了。
我知道小伙伴们看到这里肯定会想:如果我们自己创建返回 Never 的自定义视图会怎样呢?
好吧,下面就满足你们的“痴心妄想”!😃
4. 尝试创建一个自定义原生 “Never” SwiftUI 视图
自己创建一个返回 Never 的 SwiftUI 视图很简单,简直轻而易举:
struct ImpossibleView: View {var body: Never {fatalError("感受一下炸弹的威力 💥")}
}
编译没有任何问题,但是运行呢?
可以看到,不出所料 App 在启动时被毅然决然的 Crash 掉了,提示:
SwiftUI/DynamicProperty.swift:338: Fatal error: ImpossibleView may not have Body == Never
这是编译器在抗议:视图的 Body 绝对不能为 Never 类型!注意,出错信息并不是我们期望的 “感受一下炸弹的威力 💥”。
所以小伙伴们死心了吗?将 Never 作为 Body 类型是 SwiftUI 内置原生视图“神圣而不可侵犯”的特权!SwiftUI 在内部一定做了什么可以让原生视图“肆无忌惮”,我们秃头码农只能在外面“干瞪眼”了。
至此,我们彻底搞清楚了 Never 在 SwiftUI 中的“真正使命”,大家的 SwiftUI 内功又更精进了一层,棒棒哒!💯
总结
在本篇博文中,我们先是讨论了 Swift 语言中 Never 类型的起源,以及 Never 在 SwiftUI 中的“真正使命”,最后我们尝试了创建自己的 Never 视图。
感谢观赏,再会!😎
相关文章:

SwiftUI 趣谈之:绝不可能(Never)的 View!
概览 SwiftUI 的出现极大的解放了秃头码农们的生产力。SwiftUI 中众多原生和自定义视图对于我们创建精彩撩人的 App 功不可没! 不过,倘若小伙伴们略微留意过 SwiftUI 框架头文件里的源代码,就会发现里面嵌有一些奇怪 Never 类型,…...
etcd是什么
目录 1.关于etcd2.应用场景 本文主要介绍etcd 概念和基本应用场景。 1.关于etcd etcd是一个开源的、分布式的键值存储系统,用于共享配置和服务发现。它是由CoreOS团队开发的,主要用于实现分布式系统的配置管理和服务发现。 etcd的主要特性包括&#x…...

应用全局的UI状态存储AppStorage
目录 1、概述 2、StorageProp 2.1、观察变化和行为表现 3、StorageLink 3.1、观察变化和行为表现 4、从应用逻辑使用AppStorage和LocalStorage 5、从UI内部使用AppStorage和LocalStorage 6、不建议借助StorageLink的双向同步机制实现事件通知 6.1、推荐的事件通知方式…...

MySQL数据库 触发器
目录 触发器概述 语法 案例 触发器概述 触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触发并执行触发器中定义的soL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录&am…...
C语言学习之给定任意的字符串,清除字符串中的空格
实例要求:给定任意的字符串,清除字符串中的空格,并将其输出;实例分析:1、指针函数实现,需要注意指针函数的返回值是一个指针类型;2、字符类型的数组实现,循环遍历并赋给新的数组&…...
由实验数据进行函数拟合的python实现
0.引言 已知公式求参的过程,对工程而言,一般是一个线性拟合或者非线性拟合的过程。我们现在来以代码片段为例,来描述如何求参。一般这个过程会涉及超定方程的计算。这个过程,原本需要使用matlab,现在python照样可以做…...
<JavaEE> 基于 UDP 的 Socket 通信模型
目录 一、认识相关API 1)DatagramSocket 2)DatagramPacket 3)InetSocketAddress 二、UDP数据报套接字通信模型概述 三、回显客户端-服务器通信 1)服务器代码 2)客户端代码 一、认识相关API 1)Data…...

Golang 链表的基础知识
文章目录 链表链表基础知识部分链表的存储方式链表的定义链表的操作性能分析 链表 更多有关于go链表的内容可以见这篇文章链表的创建和读取 链表基础知识部分 什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,…...
webpack 常见面试题
1、什么是webpack(必会) webpack是一个打包模块化javascript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化…...

three.js实战模拟VR全景视图
文章中使用到的案例图片都来源于:Humus - Textures 里面有很多免费的资源,可以直接下载,每个资源里面都提供6个不同方位的图片,我们通过threejs稍微处理一下,就能实现以下3D效果的场景了。 <template><div …...
聊聊Spring Boot配置文件:优先级顺序、bootstrap.yml与application.yml区别详解
Spring Boot 配置文件 优先级顺序 在Spring Boot中,配置文件的优先级顺序是:bootstrap.yml > application.yml > application-{profile}.yml,其中 {profile} 表示不同的环境配置,如 dev、test、prod 等。当存在相同名称的…...
Milvus向量数据库基础用法及注意细节
1、Milvus数据类型与python对应的数据类型 Milvus Python DataType.INT64 numpy.int64 DataType.INT32 numpy.int32 DataType.INT16 numpy.int16 DataType.BOOL Boolean DataType.FLOAT numpy.float32 DataType.DOUBLE numpy.double DataType.ARRAY list DataT…...
虚拟机多开怎么设置不同IP?虚拟机设置独立IP的技巧
随着虚拟化技术的不断发展,虚拟机已经成为了许多人的必备工具。在虚拟机中,我们可以轻松地创建多个虚拟机,并在每个虚拟机中设置不同的IP地址。下面,我们将介绍如何在虚拟机中设置独立IP地址的方法。 一、虚拟机多开设置不同IP的方…...

使用Docker-镜像命令
镜像名称一般分两部分组成:[repository]:[tag] 在没有指定tag时,默认是latest,代表最新版本的镜像 目录 案例一:从DockerHub中拉取一个nginx镜像并查看 1.1. 首先去镜像仓库搜索nginx镜像,比如DockerHub 编辑 1.2.操作拉取n…...

4.3 C++对象模型和this指针
4.3 C对象模型和this指针 4.3.1 成员变量和成员函数分开存储 在C中,类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上 #include <iostream>class Person { public:Person() {mA 0;} //非静态成员变量占对象空间int mA;//静态成员变量…...

计算机网络——计算机网络的概述(一)
前言: 面对马上的期末考试,也为了以后找工作,需要掌握更多的知识,而且我们现实生活中也已经离不开计算机,更离不开计算机网络,今天开始我们就对计算机网络的知识进行一个简单的学习与记录。 目录 一、什么…...

基于多反应堆的高并发服务器【C/C++/Reactor】(中)ChannelMap 模块的实现
(三)ChannelMap 模块的实现 这个模块其实就是为Channel来服务的,前面讲了Channel这个结构体里边它封装了文件描述符。假如说我们得到了某一个文件描述符,需要基于这个文件描述符进行它对应的事件处理,那怎么办呢&…...
微信小程序实现一个音乐播放器的功能
微信小程序实现一个音乐播放器的功能 要求代码实现wxml 文件wxss 文件js文件 解析 要求 1.页面包含一个音乐列表,点击列表中的音乐可以播放对应的音乐。 2.播放中的音乐在列表中有标识,并且可以暂停或继续播放。 3.显示当前音乐的播放进度和总时长&#…...

算法基础之表达整数的奇怪方式
表达整数的奇怪方式 中国剩余定理: 求M 所有m之积 然后Mi M / mi x 如下图 满足要求 扩展中国剩余定理 找到x **使得x mod mi ai**成立 对于每两个式子 都可以推出①式 即 用扩展欧几里得算法 可以算出k1,-k2和m2–m1 判无解 : 若**(m2–m1) % d ! 0** 说明该等式无解 …...

WEB 3D技术 three.js 设置图像随窗口大小变化而变化
本文 我们来讲讲我们图层适应窗口变化的效果 可能这样说有点笼统 那么 自适应应该大家更熟悉 就是 当我们窗口发生变化说 做一些界面调整比例 例如 我们这样一个i项目界面 我们打开 F12 明显有一部分被挡住了 那么 我们可以刷新 这样是正常了 但是 我们将F12关掉 给F12的…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...