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 中主要有两种用途:

  1. 表示非正常返回方法(或函数、闭包)的返回类型(比如抛出异常、断言等);
  2. 表示没有值的类型;

比如,虽然和实际返回类型不一致,下面的 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 视图。

感谢观赏,再会!😎

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文掌握分布式锁:Mysql/Redis/Zookeeper实现

目录 一、项目准备spring项目数据库 二、传统锁演示超卖现象使用JVM锁解决超卖解决方案JVM失效场景 使用一个SQL解决超卖使用mysql悲观锁解决超卖使用mysql乐观锁解决超卖四种锁比较Redis乐观锁集成Redis超卖现象redis乐观锁解决超卖 三、分布式锁概述四、Redis分布式锁实现方案…

PyQt6 利用Pyinstaller打包发布程序

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计53条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

物联网产品设计,聊聊设备OTA的升级

物联网产品设计部分的OTA设备固件是一个非常重要的部分&#xff0c;能够实现升级用户服务、保障系统安全等功能。 在迅速变化和发展的物联网市场&#xff0c;新的产品需求不断涌现&#xff0c;因此对于智能硬件设备的更新需求就变得空前高涨&#xff0c;设备不再像传统设备一样…

数据分析基础之《numpy(5)—合并与分割》

了解即可&#xff0c;用panads 一、作用 实现数据的切分和合并&#xff0c;将数据进行切分合并处理 二、合并 1、numpy.hstack 水平拼接 # hstack 水平拼接 a np.array((1,2,3)) b np.array((2,3,4)) np.hstack((a, b))a np.array([[1], [2], [3]]) b np.array([[2], […

20231222给NanoPC-T4(RK3399)开发板的适配原厂Android10的挖掘机方案并跑通AP6398SV

20231222给NanoPC-T4(RK3399)开发板的适配原厂Android10的挖掘机方案并跑通AP6398SV 1、简略步骤&#xff1a;rootrootrootroot-X99-Turbo:~/3TB/3399-android10$ cat Rockchip_Android10.0_SDK_Release.tar.gz0* > Rockchip_Android10.0_SDK_Release.tar.gz rootrootrootro…

Spring Boot3通过GraalVM生成exe执行文件

一、安装GraalVM 1、官网&#xff1a;https://www.graalvm.org/downloads/ 2、配置环境变量 2.1、环境变量必须使用JAVA_HOME&#xff0c;否则会出现问题 2.2、在系统变量配置Path,%JAVA_HOME%\bin&#xff0c;注意必须放在顶部第一位 2.3、配置jdk的环境变量&#xff0c;在P…

高级算法设计与分析(四) -- 贪心算法

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…

python/matlab将数组以彩色图片的形式展现

python&#xff1a; plt.imshow可以将数组的值以图片的形式展示出来&#xff0c;数组的值对应着不同的颜色深浅。比如一个2X2的数组,图片里的小方块也会有2X2个。 案例1结果&#xff1a; 案例1代码&#xff1a; import numpy as np import matplotlib.pyplot as pltx np.ar…

智能优化算法应用:基于变色龙算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于变色龙算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于变色龙算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.变色龙算法4.实验参数设定5.算法结果6.参考文…

k8s启动docker容器Error: Could not find or load main class ${start-class}报错

前行提要&#xff1a; 今天部署采集点服务&#xff08;docker项目&#xff09;发现报这个错误。 提出假设&#xff1a; 1&#xff0c;配置文件错误&#xff08;工程需要配置的东西比较多&#xff09; 之后开始一一排查&#xff0c;发现配置有问题&#xff0c;但是不是这个错误…

2023的AI工具集合,google和claude被禁用解决和edge的copilot

一、前言 AI工具集合 首先&#xff0c;OpenAI的ChatGPT以其深度学习模型和强大的语言处理能力引领了AI聊天机器人的潮流。自2022年11月30日上线以来&#xff0c;它创下了100万用户的注册记录&#xff0c;并被广泛应用于全球财富500强公司。为了实现盈利&#xff0c;OpenAI发布…

Redis原理

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…