Swift 5 字符串插值-簡介

做者:Olivier Halligon,原文連接,原文日期:2018-12-15git

譯者:Nemocdz;校對:numbbbbbYousanflics;定稿:Forelaxgithub

StringInterpolation 協議最初的設計效率低下又不易擴展,爲了在後續的版本中可以將其完全重構,Swift 4 中將該協議標記爲廢棄。即將在 Swift 5 中亮相的 SE-0228 提案介紹了一種新的 StringInterpolation 設計,使得 String 有了更大的潛能。正則表達式

在 Swift 的 master 分支裏實現以後,就能夠下載一個 快照 來安裝最新的 Swift 5 工具鏈到 Xcode 中,來嘗試全新的 StringInterpolation。讓咱們來把玩一下。express

全新的 StringInterpolation 設計

我強烈建議本篇文章的讀者閱讀一下 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 類型並遵循,你就能夠用插值寫 HTML
  • 建立一個 SQLStatement 類型並遵循,你就能夠寫更簡單的 SQL 語句
  • 用字符串插值支持更多自定義格式,好比在你的插值字符串中用格式化 Double 或者 Date
  • 建立一個 RegEX 類型並遵循,你就能夠用花裏胡哨的語法寫正則表達式
  • 建立一個 AttributedString 類型並遵循,就能夠用字符串插值構建 NSAttributedString

帶來新的字符串插值設計的 Brent Royal-GordonMichael Ilseman,提供了更多例子在這個 要點列表 中。

我我的嘗試了一下支持 NSAttributedString 的實現,並想 在專門的一篇文章裏分享它的初步實現,由於我發現它很是優雅。咱們下一篇文章再見!

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 swift.gg

相關文章
相關標籤/搜索