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) -> Never

public 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 视图。

感谢观赏,再会!😎

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity中Shader旋转矩阵(二维旋转矩阵)

文章目录 前言一、旋转矩阵的原理1、我们以原点为中心&#xff0c;旋转坐标轴θ度2、求 P~2x~&#xff1a;3、求P~2y~:4、最后得到 P~2~点 的点阵5、该点阵可以拆分为以下两个矩阵相乘的结果 二、在Shader中&#xff0c;使用该旋转矩阵实现围绕 z 轴旋转1、在属性面板定义 floa…

信号与线性系统翻转课堂笔记8——周期信号的频谱

信号与线性系统翻转课堂笔记8——周期性信号的频谱 The Flipped Classroom8 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#…

nodejs+vue+微信小程序+python+PHP基于推荐算法的电影推荐系统-计算机毕业设计推荐django

信息数据的处理完全依赖人工进行操作&#xff0c;会耗费大量的人工成本&#xff0c;特别是面对大量的数据信息时&#xff0c;传统人工操作不仅不能对数据的出错率进行保证&#xff0c;还容易出现各种信息资源的低利用率与低安全性问题。更有甚者&#xff0c; 另一方面可以确保信…

系列八、VMWare无法启动CentOS7问题排查 解决

一、VMWare无法启动CentOS7 1.1、问题描述 今天在测试代码的时候&#xff0c;需要用到Linux&#xff0c;然后就打开VMWare进行启动&#xff0c;但是启动的时候发现无法启动起来&#xff0c;报了一个如下的错误&#xff1a; 出现了问题那就要解决问题&#xff0c;然后想起来前几…

【设计模式】命令模式

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、什么是命令模式&#xff1f; 二、命令模式的优点和应用场景 三、命令模式的要素和实现 3.1 命令 3.2 具体命令 3.3 接受者 …

Flink快速部署集群,体验炸了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

反序列化版本漏洞

laravel5.7反序列化漏洞 <?phpnamespace Illuminate\Foundation\Testing {class PendingCommand{public $test;protected $app;protected $command;protected $parameters;public function __construct($test, $app, $command, $parameters){$this->test $test; …

【MVT_1703230471】基于Python NLTK分词、词云、LDA主题分类及GPT情感分类

【Talk is cheap】 1 数据探索 case idcase outcome case title case text 0 Case1 cited Alpine Hardwood (Aust) Pty Ltd v Hardys Pty Lt... Ordinarily that discretion will be exercised s... 1 Case2 cited Black v Lipovac [1998] FCA 699 ; (1998) 217 AL... The gen…

tsconfig.app.json文件报红:Option ‘importsNotUsedAsValues‘ is deprecated...

在创建vue3 vite ts项目时的 tsconfig.json&#xff08;或者tsconfig.app.json&#xff09; 配置文件经常会报一个这样的错误&#xff1a; 爆红&#xff1a; Option ‘importsNotUsedAsValues’ is deprecated and will stop functioning in TypeScript 5.5. Specify compi…

【内存泄漏】编码实现内存泄漏检测功能

编码实现内存泄漏检测功能 使用脚本统计 meminfo 判断是否有内存泄漏 使用 bash 或 python 脚本循环抓取指定进程的 meminfo 保存到 txt 文件&#xff1b;使用 python 脚本解析出txt 文件中的 PSS 信息&#xff0c;借助 pyecharts 或其他可视化三方库将数据以折线图可视化&am…

【Earth Engine】协同Sentinel-1/2使用随机森林回归实现高分辨率相对财富(贫困)制图

目录 1 简介与摘要2 思路3 效果预览4 代码思路5 完整代码6 后记 1 简介与摘要 最近在做一些课题&#xff0c;需要使用Sentinel-1/2进行机器学习制图。 然后想着总结一下相关数据和方法&#xff0c;就花半小时写了个代码。 然后再花半小时写下这篇博客记录一下。 因为基于多次拍…

学校和老师如何制作自己免费的成绩查询系统

在当今数字化的时代&#xff0c;许多学校都采用信息技术来管理和提高工作效率。其中&#xff0c;成绩查询系统是一个非常实用的工具&#xff0c;它可以让老师和学生们快速、方便地查询成绩。那么&#xff0c;学校和老师如何制作自己免费的成绩查询系统呢&#xff1f;本文将为你…

【Amazon 实验①】使用 Amazon CloudFront加速Web内容分发

文章目录 实验架构图1. 准备实验环境2. 创建CloudFront分配、配置动、静态资源分发2.1 创建CloudFront分配&#xff0c;添加S3作为静态资源源站2.2 为CloudFront分配添加动态源站 在本实验——使用CloudFront进行全站加速中&#xff0c;将了解与学习Amazon CloudFront服务&…

Python办公自动化Day1

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;xlwt创建Excelxlrd读取Excelxlutils修改Excelxlwt设置样式常见的字体样式单元格宽高内容对齐方式设置单元格边框设置背景颜色样式整合起来的写法 文章声明⭐⭐⭐ 该文章为我&#xff08;有编程语言基础&#xff0c;非编…

RabbitMQ笔记(高级篇)

RabbitMQ笔记_高级篇 问题代码准备1. 新建生产者2. 新建消费者 RabbitMQ 高级特性1. 消息的可靠投递☆1.1 两种模式1.2 测试confirm 确认模式1.3 测试return 退回模式1.4 小结 2. Consumer ACK☆2.1 三种ACK2.2 测试手动ACK2.3 小结2.4 消息可靠性总结 3. 消费端限流测试消费端…

旅游海报图怎么做二维码展示?扫码即可查看图片

现在旅游攻略的海报可以做成二维码印刷在宣传单单页或者分享给用户来了解目的地的实际情况&#xff0c;出行路线、宣传海报等。用户只需要扫描二维码就可以查看内容&#xff0c;更加的方便省劲&#xff0c;那么旅游海报的图片二维码制作的技巧有哪些呢&#xff1f;使用图片二维…

【算法设计与分析】——动态规划算法

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

关于“Python”的核心知识点整理大全36

目录 13.4.4 向下移动外星人群并改变移动方向 game_functions.py alien_invasion.py 13.5 射杀外星人 13.5.1 检测子弹与外星人的碰撞 game_functions.py alien_invasion.py 13.5.2 为测试创建大子弹 13.5.3 生成新的外星人群 game_functions.py alien_invasion.py …

【github】github设置项目为私有

点击setting change to private 无脑下一步

为什么c++的开源库那么少?

为什么c的开源库那么少&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「c的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&…