Swift ExpressibleBy协议:它们是什么以及它们如何在编译器内部工作

ExpressibleBy表示Swift标准库中的一系列协议,它允许您直接从令牌文字实例化对象,如字符串,数字,浮点等,如果对象可以像这样“表达”。例如,以下是在Swift中创建URL的常规方法:

点击此处进交流群 有技术的来闲聊 没技术的来学习

func getURL() -> URL
复制代码

但是,为了防止每次都必须使用此初始化程序,您可以说可以使用ExpressibleByStringLiteral以下命令直接从其URL字符串表示URL :

extension URL: ExpressibleByStringLiteral {
复制代码

这允许我们重构以使用除字符串标记之外的任何其他内容来创建URL:getURL()

func getURL() -> URL
复制代码

标准库包含以下ExpressibleBy协议:

ExpressibleByNilLiteral:表达方式nil。 * ExpressibleByIntegerLiteral:由数字标记表示10。 * ExpressibleByFloatLiteral:由浮点标记表示2.5。 * ExpressibleByBooleanLiteral:表达方式。 * :从单个unicode标量表示。用法示例是和。 * :与UnicodeScalar类似,但由一系列标量(字形集群)而不是单个标量组成。 * :由字符串标记表示。 * :由数组标记表示。 * :由字典标记表示。true/false ExpressibleByUnicodeScalarLiteral Character String ExpressibleByExtendedGraphemeClusterLiteral ExpressibleByStringLiteral "SwiftRocks" ExpressibleByArrayLiteral [1,2,3] ExpressibleByDictionaryLiteral ["name": "SwiftRocks"]

简而言之,您可以使用这些协议隐藏不必要的实现细节,并可能隐藏更复杂类型的丑化初始化程序。一个示例用例是Apple的SourceKit-LSP如何使用它们来表示任意参数 - 因为Any类型不符合CodableCommandArgumentType枚举用于表示未知参数:

public enum CommandArgumentType: Hashable, ResponseType {
复制代码

但是,因为我们正在处理枚举,所以表示一个参数将导致不那么漂亮的代码行:

func getCommandArguments() -> CommandArgumentType {
复制代码

幸运的是,我们可以ExpressibleBy用来为枚举提供更好看的替代方案:

extension CommandArgumentType: ExpressibleByNilLiteral {
复制代码

这使我们可以用更容易阅读的令牌重写。getCommandArguments()

func getCommandArguments() -> CommandArgumentType {
复制代码

内部如何工作

但令牌如何成为完整类型?与所有编译器魔法一样,我们可以通过拦截Swift的编译步骤来揭示正在发生的事情。

以第一种方法为例,让我们首先看看Swift如何处理ExpressibleBy对象。如果我们使用参数手动编译代码,我们可以提取代码的Swift中间语言(SIL)版本 - 在LLVM开始之前,Swift中的最终编译步骤。getURL() -emit-sil

swiftc -emit-sil geturl.swift
复制代码

我编辑的输出使其更易于阅读,如下所示:

sil hidden @$s3bla6getURL10Foundation0C0VyF : $@convention(thin) () -> @out URL {
bb0(%0 : $*URL):
复制代码

这是方法正在做的事情:

1:创建string_literal令牌 2:String从文字 3 创建类型:使用 4 调用:返回URLURL.init(stringLiteral:) String

正如人们所料,编译器通过用String相关的ExpressibleBy初始化程序替换代码行来实现这种魔力。万岁为编译魔术!

现在,为了找到编译器中发生这种情况的位置,我们可以grep在Swift源中提到“ExpressibleBy”,它将指向CSApply.cpp中的几个位置。简而言之,文字的所有用法都被转换为它们的ExpressibleBy等价物,包括“本身就是文字的表达”(例如,a Int本身就是一个ExpressibleByIntegerLiteral)。当Swift的类型检查器到达文字时,它会获得相关协议类型的实例和初始化程序的名称,这可以从我们正在查看的文字中确定:

Expr *visitNilLiteralExpr(NilLiteralExpr *expr) {
复制代码

根据手中的信息,类型检查程序调用convertLiteralInPlace来代替充分表达具有同等ExpressibleBy初始化。该方法本身做了很多事情,但这里有一些值得注意的事情:如果我们看看KnownProtocols.def,我们可以看到所有文字都有默认类型:

EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
复制代码

这意味着如果表达式没有类型或者类型不符合协议,则文本的真实类型将被分配给默认的类型一致性。例如,如果我删除了一致性,则SIL代码将显示使用内部初始化程序:getURL()``String

func getURL() -> URL {
复制代码

这不仅允许你编写类似的非类型化表达式,而且还可以用于UI原因 - 由于这一点,在后面的过程中,前面的例子将导致我们用户友好的编译错误。let foo = "bar" getURL() Cannot convert value of type'String' to specified type 'URL'


小编这里有大量的书籍和面试资料哦(点击下载

原文转载地址: swiftrocks.com/swift-expre…

转载于:https://juejin.im/post/5d369f07f265da1b84672536

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值