做者: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。