代码编织梦想

什么是 typealias?

当我们回忆那些 Swift 强大的语言特性时,很少有人会首先想到 typealias。然而,许多情况下类型别名会很有用。本文将简要介绍 typealias 是什么,如何定义它,并列出多个示例说明如何在自己的代码中使用它们。让我们开始深入了解吧!

顾名思义,typealias是特定类型的别名。类型,例如IntDoubleUIViewController 或一种自定义类型。Int32Int8 是不同的类型。换句话说,类型别名在你的代码库里插入现有类型的另一个名称。例如:

typealias Money = Int

Int 类型创建别名。这样就可以在代码中的任何地方使用 Money,就像是Int一样:

struct Bank {  typealias Money = Int  private var credit: Money = 0  mutating func deposit(amount: Money) {    credit += amount  }  mutating func withdraw(amount: Money) {    credit -= amount  }}

上面有一个结构体Bank 来管理钱。但是,没有使用 Int作为金额,而是使用 Money 类型。可以看出 +=-=运算符仍然可以按预期工作。

还可以混合使用类型别名和原始类型,以及匹配二者。可以这么做是因为对于 Swift 编译器来说,它们都解析为同一个东西:

struct Bank {  typealias DepositMoney = Int  typealias WithdrawMoney = Int  private var credit: Int = 0  mutating func deposit(amount: DepositMoney) {    credit += amount  }  mutating func withdraw(amount: WithdrawMoney) {    credit -= amount  }}

在这里,我们混合使用了 Int及其不同自定义类型别名 DepositMoneyWithdrawMoney

泛 型 类 型 别 名 泛型类型别名

除上述内容外,类型别名也可以具有泛型参数:

typealias MyArray<T> = Array<T>let newArray: MyArray = MyArray(arrayLiteral: 1, 2, 3)

上面,为 MyArray定义了一个类型别名,该别名与常规数组一样。最后,类型别名的泛型参数甚至可以具有约束。想象一下,我们希望新的 MyArray 只保留遵循 StringProtocol 的类型:

typealias MyArray<T> = Array<T> where T: StringProtocol

这是一个不错的特性,你可以快速为特定类型定义数组,而不必将 Array 子类化。说到这里,让我们看一下类型别名的一些实践应用。

实践应用

更 清 晰 的 代 码 更清晰的代码

第一个,同时也显而易见的用例,我们已经简要介绍过了。类型别名可以使代码更具含义。在 typealias Money = Int示例中,我们引入了 Money 类型——一个清晰的概念。像 let amount: Money = 0这样来使用它,比 let amount: Int = 0更容易理解。在第一个示例中,你立刻就明白这是金钱de数额。而在第二个示例中,它可以是任何东西:自行车的数量、字符的数量、甜甜圈的数量——这谁知道!

这显然不是都必要的。如果函数签名已经清楚地说明了参数的类型(func orderDonuts(amount: Int)),那么包含其他的类型别名将是不必要的开销。另一方面,对于变量和常量来说,它通常可以提高可读性并极大地帮助编写文档。

更 简 单 的 可 选 闭 包 更简单的可选闭包

Swift 中的可选闭包有点笨拙。接受一个 Int 参数并返回 Int 的闭包的常规定义如下所示:

func handle(action: (Int) -> Int) { ... }

现在,如果要使此闭包为可选型,则不能仅添加问号:

func handle(action: (Int) -> Int?) { ... }

毕竟,这不是一个可选型的闭包,而是一个返回可选 Int的闭包。正确的方法是添加括号:

func handle(action: ((Int) -> Int)?) { ... }

如果有多个这样的闭包,这将变得尤为难看。下面,有一个函数,它可以处理成功和失败情况,以及随着操作的进行调用一个附加的闭包。

func handle(success: ((Int) -> Int)?,            failure: ((Error) -> Void)?,            progress: ((Double) -> Void)?) {    }

这小段代码包含很多括号。由于我们不打算成为 lisper(译者注:lisp 语言使用者),因此想通过对不同的闭包使用类型别名来解决此问题:

typealias Success = (Int) -> Inttypealias Failure = (Error) -> Voidtypealias Progress = (Double) -> Voidfunc handle2(success: Success?, failure: Failure?, progress: Progress?) { ... }

实际上,这个函数看起来确实更具可读性。虽然这很好,但我们确实通过使用三行 typealias 引入了其他语法。但是,从长远来看,这实际上可能对我们有帮助,就像我们将在接下来看到的。

集 中 定 义 集中定义

这些特定类型不仅仅可以用在前面示例的那些操作处理器中。下面是经过略微修改,更符合实际使用的操作处理器类:

final class Dispatcher {  private var successHandler: ((Int) -> Void)?  private var errorHandler: ((Error) -> Void)?    func handle(success: ((Int) -> Void)?, error: ((Error) -> Void)?) {    self.successHandler = success    self.errorHandler = error    internalHandle()  }    func handle(success: ((Int) -> Void)?) {   self.successHandler = success    internalHandle()  }    func handle(error: ((Int)-> Void?)) {    self.errorHandler = error    internalHandle()  }    private func internalHandle() {   ...  }}

该结构体引入了两个闭包,一个用于成功情况,一个用于错误情况。但是,我们还希望提供更方便的函数,调用其中一个处理器即可。在上面的示例中,如果要向成功和错误处理器添加另一个参数(例如 HTTPResponse),那么需要更改很多代码。在三个地方,((Int) -> Void)?需要变成((Int, HTTPResponse) -> Void)?。错误处理器也是一样的。通过使用多个类型别名,可以避免这种情况,只需要在一个地方修改类型:

final class Dispatcher {  typealias Success = (Int, HTTPResponse) -> Void  typealias Failure = (Error, HTTPResponse) -> Void  private var successHandler: Success?  private var errorHandler: Failure?    func handle(success: Success?, error: Failure?) {    self.successHandler = success    self.errorHandler = error    internalHandle()  }    func handle(success: Success?) {   self.successHandler = success    internalHandle()  }    func handle(error: Failure?) {    self.errorHandler = error    internalHandle()  }    private func internalHandle() {   ...  }}

这不仅易于阅读,而且随着在更多地方使用该类型,它也会继续发挥它的作用。

泛 型 别 名 泛型别名

类型别名也可以是泛型的。一个简单的用例是强制执行具有特殊含义的容器。假设我们有一个处理图书的应用。一本书由章节组成,章节由页面组成。从根本上讲,这些只是数组。

下面是 typealias

struct Page {}typealias Chapter = Array<Page>typealias Book = Array<Chapter>

与仅使用数组相比,这有两个好处

1.该代码更具解释性。

2.包装页面的数组只能包含页面,而不能包含其它的。回顾我们先前使用成功和失败处理程序的示例,我们可以通过使用泛型处理程序来进一步改进:

typealias Handler<In> = (In, HTTPResponse?, Context) -> Voidfunc handle(success: Handler<Int>?,             failure: Handler<Error>?,           progress: Handler<Double>?,)

这样的组合确实非常棒。这使我们能够编写一个更简单的函数,并可以在一个地方编辑 Handler

这种方法对于自定义的类型也非常有用。你可以创建一个泛型定义,然后定义详细的类型别名:

struct ComputationResult<T> {  private var result: T}typealias DataResult = ComputationResult<Data>typealias StringResult = ComputationResult<String>typealias IntResult = ComputationResult<Int>

再说一遍,类型别名允许我们编写更少的代码并简化代码中的定义。

像 函 数 一 样 的 元 组 像函数一样的元组

同样,可以使用泛型和元组来定义类型,而不是必须用结构体。下面,我们设想了一种遗传算法的数据类型,它可以在多代中修改其值T

typealias Generation<T: Numeric> = (initial: T, seed: T, count: Int, current: T)

如果定义这样的类型别名,则实际上可以像初始化一个结构体那样对其进行初始化:

let firstGeneration = Generation(initial: 10, seed: 42, count: 0, current: 10)

尽管它看起来确实像一个结构体,但它只是一个元组的类型别名。

组 合 协 议 组合协议

有时,你会遇到一种情况,你有多个协议,而且需要使用一个特定类型来把这些协议都实现。这种情况通常发生在当你定义了一个协议层来提高灵活性时。

protocol CanRead {}protocol CanWrite {}protocol CanAuthorize {}protocol CanCreateUser {}typealias Administrator = CanRead & CanWrite & CanAuthorize & CanCreateUsertypealias User = CanRead & CanWritetypealias Consumer = CanRead

在这里,我们定义了权限层。管理员可以做所有事情,用户可以读写,而消费者只能读。

关 联 类 型 关联类型

这超出了本文的范围,但是协议的关联类型也可以通过类型别名来定义:

protocol Example { associatedtype Payload: Numeric}struct Implementation: Example {  typealias Payload = Int}

缺点

尽管类型别名是一个非常有用的功能,但它们有一个小缺点:如果你不熟悉代码库,那么对下面这两个定义的理解会有很大区别。

func first(action: (Int, Error?) -> Void) {}func second(action: Success) {}

第二个不是立即就能明白的。Success 是什么类型?如何构造它?你必须在 Xcode 中按住 Option 单击它,以了解它的功能和工作方式。这会带来额外的工作量。如果使用了许多类型别名,则将花费更多的时间。这没有很好的解决方案,(通常)只能依赖于用例。

文 章 来 源 于 网 络 文章来源于网络
原文作者:iOS一叶

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/qq_45765503/article/details/111033250

2020 — iOS 面试败北感悟-爱代码爱编程

原文作者:Castie1 原文链接:https://juejin.im/post/6844903591010910216 日常扯淡 去年7月, 第一次面试大公司: 饿了么, 收到大公司的召唤非常的兴奋, 觉得自己翻身的机会终于要来了, 兴冲冲的跑去面试, 以为会和一般初级iOS面试的题目相同, 没有做任何的准备, 其实也不知道准备什么, 记得那时候

iOS绘制仪表盘,游标沿圆形轨迹移动动画-爱代码爱编程

最近碰到一个需求,需要画一个仪表盘的页面。图上所示。 计算角度 圆弧部分还好,用CAShapeLayer+UIBezierPath曲线,只要确定好圆心部分和左右两边的角度就行。这里正好说明一下 - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(C

iOS动画开发-Lottie动画实战-爱代码爱编程

介绍 如果你还不知道Lottie是什么, 那你真的out了. 如果把iOS动画分为两类: 交互式动画, 播放式动画, 那么其中的播放动画完全可以使用Lottie来完成, 例如: 作为收藏按钮, 是不是很活泼? 返回与菜单之间的切换, 生动有趣! 还有各种形变动画. 这里先放上Lottie的地址: github.com/airbnb/

Flutter -- static route and dynamic route-爱代码爱编程

前言 每个应用都有很多个页面,在 Flutter中同样也有很多页面,被称之为路由(route),route 的管理是通过堆栈来实现的。也就是说,基本的使用方法是 push 和 pop。路由的类型分为 static route 和 dynamic route,下面我们分别对这两种 route 进行说明。 static route 顾名思义,static

ps安装插件提示“无法加载扩展,因为它未正确签署”怎么办?PS插件未经签署解决方法-爱代码爱编程

在使用PS安装使用插件的时候你有没有遇到过提示“无法加载扩展,因为它未正确签署”,这时候怎么解决呢?ps中安装了插件,但是没办法使用这个扩展插件,该怎么办呢?下面我们就来看看PS插件未经签署解决方法。 PS插件未经签署解决方法 1、选择应用程序-实用工具-终端 2、打开终端输入ps 2021 Mac版命令行: defaults write com.ado

右键点按,为你的访达菜单展开更多可能性-爱代码爱编程

访达Finder中最实用的一些菜单,其实就藏在你的指针里面。只要按住 Control 点按或右键点按文稿、文件夹或窗口,就能让它们现身了。 点按不同的对象,这些菜单中的选项也有所不同。下面就来看看它们的使用技巧,以及让它们更好地发挥功能的 App。 压缩文件 要为文件、文件夹或一组文件创建 ZIP 归档,按住 Control 点按它,然后在菜单中选择

swift基础day7---泛型、不透明类型、自动引用计数、内存安全、访问控制-爱代码爱编程

泛型能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。 泛型函数 泛型版本的函数使用占位符类型名(这里叫做 T ),而不是 实际类型名(例如 Int、String 或 Double),占位符类型名并不关心 T 具体的类型,但它要求 a 和b 必须是相同的类型,T 的实际类型由每次调用 swapTwoValues(:😃 来决定。 //

推荐我老师的博客精品文章汇总(持续更新)-爱代码爱编程

文章目录 前言I、 iOS进阶1.1 常用动画1.2 蓝牙打印1.3 本地化II、iOS安全与逆向2.1 iOS逆向2.2 iOS安全2.2.1 接口安全III、 JAVAIV 、iOS基础5.1 系统适配5.2 基础功能5.3 自定义视图5.4 支付相关基础知识V、阅读与写作VI、理解计算机see also接口安全设计的Checklist 前

结构体与类,值类型与引用类型-爱代码爱编程

结构体与类的重要区别 Swift中,结构体与类的一个重要区别就是结构体是一个值类型而类是一个引用类型。如果定义一个全局变量的结构体,其数据将存储在数据段;而如果定义一个全局变量的类对象,数据段将存放其在堆空间的指针,其实际的数据将存储在堆空间(和类型信息与引用计数一起)。 值类型与引用类型 值类型与引用类型之间的一个重要差异在于,如果将一个值类型赋值

iOS LeetCode☞合并两个有序链表-爱代码爱编程

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 代码如下 func mergeTwoLists(_ l1: ListNode?, _ l2: ListNo

iOS LeetCode☞括号生成-爱代码爱编程

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例: 输入:n = 3 输出:[ "((()))", "(()())", "(())()", "()(())", "()()()" ] 题解: 为了生成所有序列,我们可以使

iOS马甲包上架招式-爱代码爱编程

一、什么是马甲包 马甲包是利用App store 规则漏洞,通过技术手段,多次上架同一款产品的方法。马甲包和主产品包拥有同样的内容和功能,除了icon和应用名称不能完全一致,其他基本一致。 二、为什么做马甲包,做马甲包有什么好处? 1、导量、刷榜、增加关键字覆盖 一个App的关键字是有限的,马甲包能增加我们的搜索关键词,增加我们的App被用户搜索和下