做者:Olivier Halligon,原文连接,原文日期:2018-12-15git
译者:Nemocdz;校对:numbbbbb,Yousanflics;定稿:Forelaxgithub
StringInterpolation
协议最初的设计效率低下又不易扩展,为了在后续的版本中可以将其完全重构,Swift 4 中将该协议标记为废弃。即将在 Swift 5 中亮相的 SE-0228 提案介绍了一种新的 StringInterpolation
设计,使得 String 有了更大的潜能。正则表达式
在 Swift 的 master
分支里实现以后,就能够下载一个 快照 来安装最新的 Swift 5 工具链到 Xcode 中,来尝试全新的 StringInterpolation
。让咱们来把玩一下。express
我强烈建议本篇文章的读者阅读一下 SE-0228 提案,感觉一下新 API 的背后的设计思路和动机。swift
要让一个类型遵循 ExpressibleByStringInterpolation
,最基本的你须要:markdown
StringInterpolation
的子类型,这个子类型遵循 StringInterpolationProtocol
并将负责解释插值appendLiteral(_ literal: String)
方法,再选择一个或多个你本身想要支持的 appendInterpolation(...)
签名的方法StringInterpolation
子类型会做为“构造器”服务于你的主类型,而后编译器会调用那些 append…
方法一步一步地构造对象init(stringInterpolation: StringInterpolation)
,它会用上一步的结果来实例化它本身。你能够实现任何你喜欢的 appenInterpolation(...)
方法,这意味着你能够任意选择支持什么插值。这是一个带来巨大的可能性的超强功能。app
举个例子,若是你实现了 func appendInterpolation(_ string: String, pad: Int)
,那么意味着你将能够用相似这样的插值:"Hello \(name, pad: 10), how are you?"
来构造你的类型。插值只须要匹配你的 StringInterpolation
子类型其中一个支持的 appendInterpolation
方法签名。工具
让我用一个简单的例子来演示一下插值是如何运做的。一块儿来构造一个容许引用 issue 编号和用户的 GitHubComment
类型吧。spa
这个例子的目标是作到相似下面的写法:翻译
let comment: GitHubComment = """
See \(issue: 123) where \(user: "alisoftware") explains the steps to reproduce.
"""
复制代码
因此咱们该怎么实现它呢?
首先,让咱们声明基本的结构体 struct GitHubComment
并让它遵循 ExpressibleByStringLiteral
(由于 ExpressibleByStringInterpolation
继承自这个协议因此咱们将它的实现抽离)和 CustomStringConvertible
(为了 debug 时友好地在控制台中打印)。
struct GitHubComment {
let markdown: String
}
extension GitHubComment: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
self.markdown = value
}
}
extension GitHubComment: CustomStringConvertible {
var description: String {
return self.markdown
}
}
复制代码
而后,咱们让 GitHubComment
遵循 ExpressibleByStringInterpolation
。这意味着在剩下须要实现的功能,将由一个 StringInterpolation
子类型来完成:
首先初始化它本身:init(literalCapacity: Int, interpolationCount: Int)
提供给你保留一部分数据到缓冲区的能力,在一步步构造类型时就会用到这个能力。在这个例子中,咱们能够在构造实例的时候,简单用一个 String
并往它上面追加片断,不过这里我采用一个 parts: [String]
来代替,以后再将它组合起来
实现 appendLiteral(_ string: String)
逐个追加文本到 parts
里
实现 appendInterpolation(user: String)
在遇到 \(user: xxx)
时逐个追加 markdown 表示的用户配置连接
实现 appendInterpolation(issue: Int)
逐个追加用 markdown 表示的 issue 连接
而后在 GitHubComment
上实现 init(stringInterpolation: StringInterpolation)
将 parts
构形成一个评论
extension GitHubComment: ExpressibleByStringInterpolation {
struct StringInterpolation: StringInterpolationProtocol {
var parts: [String]
init(literalCapacity: Int, interpolationCount: Int) {
self.parts = []
// - literalCapacity 文本片断的字符数 (L)
// - interpolationCount 插值片断数 (I)
// 咱们预计一般结构会是像 "LILILIL"
// — e.g. "Hello \(world, .color(.blue))!" — 所以是 2n+1
self.parts.reserveCapacity(2*interpolationCount+1)
}
mutating func appendLiteral(_ literal: String) {
self.parts.append(literal)
}
mutating func appendInterpolation(user name: String) {
self.parts.append("[\(name)](https://github.com/\(name))")
}
mutating func appendInterpolation(issue number: Int) {
self.parts.append("[#\(number)](issues/\(number))")
}
}
init(stringInterpolation: StringInterpolation) {
self.markdown = stringInterpolation.parts.joined()
}
}
复制代码
这就完事了!咱们成功了!
注意,由于那些咱们实现了的 appendInterpolation
方法签名,咱们容许使用 Hello \(user: "alisoftware")
但不能使用 Hello \(user: 123)
,由于 appendInterpolation(user:)
指望一个 String
做为形参。相似的是,在你的字符串中 \(issue: 123)
只能容许一个 Int
由于 appendInterpolation(issue:)
采用一个 Int
做为形参。
实际上,若是你尝试在你的 StringInterpolation
子类中用不支持的插值,编译器会给你提示报错:
let comment: GitHubComment = """
See \(issue: "bob") where \(username: "alisoftware") explains the steps to reproduce.
"""
// ^~~~~ ^~~~~~~~~
// 错误: 没法转换 ‘String’ 类型的值到指望的形参类型 ‘Int’
// 错误: 调用 (have 'username:', expected 'user:')实参标签不正确
复制代码
这个新的设计打开了一大串脑洞让你去实现本身的 ExpressibleByStringInterpolation
类型。这些想法包括:
HTML
类型并遵循,你就能够用插值写 HTMLSQLStatement
类型并遵循,你就能够写更简单的 SQL 语句Double
或者 Date
值RegEX
类型并遵循,你就能够用花里胡哨的语法写正则表达式AttributedString
类型并遵循,就能够用字符串插值构建 NSAttributedString
带来新的字符串插值设计的 Brent Royal-Gordon 和 Michael Ilseman,提供了更多例子在这个 要点列表 中。
我我的尝试了一下支持 NSAttributedString
的实现,并想 在专门的一篇文章里分享它的初步实现,由于我发现它很是优雅。咱们下一篇文章再见!
本文由 SwiftGG 翻译组翻译,已经得到做者翻译受权,最新文章请访问 swift.gg。