StringInterpolation

字符串表達協(xié)議

ExpressibleByStringLiteral協(xié)議

Swift
/// A type that can be initialized with a string literal.
/// The `String` and `StaticString` types conform to the
/// `ExpressibleByStringLiteral` protocol. You can initialize a variable or
/// constant of either of these types using a string literal of any length.
///     let picnicGuest = "Deserving porcupine"
/// Conforming to ExpressibleByStringLiteral
/// ========================================
/// To add `ExpressibleByStringLiteral` conformance to your custom type,
/// implement the required initializer.
public protocol ExpressibleByStringLiteral : ExpressibleByExtendedGraphemeClusterLiteral {
    /// A type that represents a string literal.
    /// Valid types for `StringLiteralType` are `String` and `StaticString`. associatedtype StringLiteralType : _ExpressibleByBuiltinStringLiteral
    /// Creates an instance initialized to the given string value.
    /// - Parameter value: The value of the new instance.
    init(stringLiteral value: Self.StringLiteralType)
}
// 默認實現(xiàn)的 init方法
extension ExpressibleByStringLiteral where Self.ExtendedGraphemeClusterLiteralType == Self.StringLiteralType {
    /// Creates an instance initialized to the given value.
    /// - Parameter value: The value of the new instance.
    public init(extendedGraphemeClusterLiteral value: Self.StringLiteralType)

}

ExpressibleByStringInterpolation協(xié)議

Swift
public protocol ExpressibleByStringInterpolation : ExpressibleByStringLiteral {
    /// The type each segment of a string literal containing interpolations
    /// should be appended to.
    /// The `StringLiteralType` of an interpolation type must match the
    /// `StringLiteralType` of the conforming type.
    associatedtype StringInterpolation : StringInterpolationProtocol = DefaultStringInterpolation where Self.StringLiteralType == Self.StringInterpolation.StringLiteralType
    /// Creates an instance from a string interpolation.
    /// Most `StringInterpolation` types will store information about the
    /// literals and interpolations appended to them in one or more properties.
    /// `init(stringInterpolation:)` should use these properties to initialize
    /// the instance.
    /// - Parameter stringInterpolation: An instance of `StringInterpolation`
    ///             which has had each segment of the string literal appended
    ///             to it.
    init(stringInterpolation: Self.StringInterpolation)
}

// 默認實現(xiàn)的 init方法(注意 Where 條件)
extension ExpressibleByStringInterpolation where Self.StringInterpolation == DefaultStringInterpolation {
    /// Creates a new instance from an interpolated string literal.
    /// Don't call this initializer directly. It's used by the compiler when
    /// you create a string using string interpolation. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = """
    ///                   If one cookie costs \(price) dollars, \
    ///                   \(number) cookies cost \(price * number) dollars.
    ///                   """
    ///     // message == "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
    public init(stringInterpolation: DefaultStringInterpolation)
}

字符串相關(guān)

StringProtocol協(xié)議

Swift
/// A type that can represent a string as a collection of characters.
/// Do not declare new conformances to `StringProtocol`. Only the `String` and
/// `Substring` types in the standard library are valid conforming types.
public protocol StringProtocol : BidirectionalCollection, Comparable, ExpressibleByStringInterpolation, Hashable, LosslessStringConvertible, TextOutputStream, TextOutputStreamable where Self.Element == Character, Self.Index == String.Index, Self.StringInterpolation == DefaultStringInterpolation, Self.SubSequence : StringProtocol

摘掉和本文不相關(guān)的協(xié)議和約束措嵌,剩下

Swift
/// A type that can represent a string as a collection of characters.
/// Do not declare new conformances to `StringProtocol`. Only the `String` and
/// `Substring` types in the standard library are valid conforming types.
public protocol StringProtocol : ExpressibleByStringInterpolation

String 結(jié)構(gòu)體

Swift
// String 實現(xiàn)協(xié)議 ExpressibleByStringInterpolation init 方法
@frozen public struct String {
    @inlinable public init()
    /// Creates a new instance from an interpolated string literal.
    /// Do not call this initializer directly. It is used by the compiler when
    /// you create a string using string interpolation. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = """
    ///                   If one cookie costs \(price) dollars, \
    ///                   \(number) cookies cost \(price * number) dollars.
    ///                   """
    ///     print(message)
    ///     // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
    @inlinable public init(stringInterpolation: DefaultStringInterpolation)
    ......
}
Swift
// String 實現(xiàn)協(xié)議 ExpressibleByStringInterpolation 關(guān)聯(lián)類型
extension String : StringProtocol {
    /// The type each segment of a string literal containing interpolations
    /// should be appended to.
    /// The `StringLiteralType` of an interpolation type must match the
    /// `StringLiteralType` of the conforming type.
    public typealias StringInterpolation = DefaultStringInterpolation

}

// String 實現(xiàn)協(xié)議 ExpressibleByStringLiteral
//(ExpressibleByStringInterpolation 的繼承)
extension String : ExpressibleByStringLiteral {
    /// Creates an instance initialized to the given string value.
    /// Do not call this initializer directly. It is used by the compiler when you
    /// initialize a string using a string literal. For example:
    ///     let nextStop = "Clark & Lake"
    /// This assignment to the `nextStop` constant calls this string literal
    /// initializer behind the scenes.
    @inlinable public init(stringLiteral value: String)
    /// A type that represents an extended grapheme cluster literal.
    /// Valid types for `ExtendedGraphemeClusterLiteralType` are `Character`,
    /// `String`, and `StaticString`.
    public typealias ExtendedGraphemeClusterLiteralType = String
    /// A type that represents a string literal.
    /// Valid types for `StringLiteralType` are `String` and `StaticString`.
    public typealias StringLiteralType = String
    /// A type that represents a Unicode scalar literal.
    /// Valid types for `UnicodeScalarLiteralType` are `Unicode.Scalar`,
    /// `Character`, `String`, and `StaticString`.
    public typealias UnicodeScalarLiteralType = String
}

字符串插值協(xié)議與默認的插值器(暫且叫它)

StringInterpolationProtocol協(xié)議

Swift
public protocol StringInterpolationProtocol {
    /// The type that should be used for literal segments.
    associatedtype StringLiteralType : _ExpressibleByBuiltinStringLiteral
    init(literalCapacity: Int, interpolationCount: Int)
    mutating func appendLiteral(_ literal: Self.StringLiteralType)
}

DefaultStringInterpolation 結(jié)構(gòu)體(插值器)

Swift
@frozen public struct DefaultStringInterpolation : StringInterpolationProtocol, Sendable {
    /// Creates a string interpolation with storage pre-sized for a literal
    /// with the indicated attributes.
    /// Do not call this initializer directly. It is used by the compiler when
 /// interpreting string interpolations.
    @inlinable public init(literalCapacity: Int, interpolationCount: Int)
    /// Appends a literal segment of a string interpolation.
    /// Do not call this method directly. It is used by the compiler when
    /// interpreting string interpolations.
    @inlinable public mutating func appendLiteral(_ literal: String)
    /// Interpolates the given value's textual representation into the
    /// string literal being created.
    /// Do not call this method directly. It is used by the compiler when
    /// interpreting string interpolations. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = """
    ///                   If one cookie costs \(price) dollars, \
    ///                   \(number) cookies cost \(price * number) dollars.
    ///                   """
    ///     print(message)
    ///     // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
    @inlinable public mutating func appendInterpolation<T>(_ value: T) where T : CustomStringConvertible, T : TextOutputStreamable
    /// Interpolates the given value's textual representation into the
    /// string literal being created.
    /// Do not call this method directly. It is used by the compiler when
    /// interpreting string interpolations. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = "If one cookie costs \(price) dollars, " +
    ///                   "\(number) cookies cost \(price * number) dollars."
    ///     print(message)
    ///     // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
    @inlinable public mutating func appendInterpolation<T>(_ value: T) where T : TextOutputStreamable
    /// Interpolates the given value's textual representation into the
    /// string literal being created.
    /// Do not call this method directly. It is used by the compiler when
    /// interpreting string interpolations. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = """
    ///                   If one cookie costs \(price) dollars, \
    ///                   \(number) cookies cost \(price * number) dollars.
    ///                   """
    ///     print(message)
    ///     // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."

    @inlinable public mutating func appendInterpolation<T>(_ value: T) where T : CustomStringConvertible
    /// Interpolates the given value's textual representation into the
    /// string literal being created.
    /// Do not call this method directly. It is used by the compiler when
    /// interpreting string interpolations. Instead, use string
    /// interpolation to create a new string by including values, literals,
    /// variables, or expressions enclosed in parentheses, prefixed by a
    /// backslash (`\(`...`)`).
    ///     let price = 2
    ///     let number = 3
    ///     let message = """
    ///                   If one cookie costs \(price) dollars, \
    ///                   \(number) cookies cost \(price * number) dollars.
    ///                   """
    ///     print(message)
    ///     // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars."
    @inlinable public mutating func appendInterpolation<T>(_ value: T)
    public mutating func appendInterpolation(_ value: Any.Type)
    /// The type that should be used for literal segments.
    public typealias StringLiteralType = String
}

實例

案例一:實現(xiàn)自定義字符串插值器,通過插值器創(chuàng)建實例

Swift
struct Person {
  let des: String
}
extension Person: ExpressibleByStringLiteral {
  init(stringLiteral value: String) {
    self.des = value
  }
}
extension Person: CustomStringConvertible {
  var description: String {
    return self.des
  }
}
// 字符串插值器(來自網(wǎng)絡(luò))
struct StringInterpolationC: StringInterpolationProtocol {
    var parts: [String]
    init(literalCapacity: Int, interpolationCount: Int) {
        self.parts = []
        // - literalCapacity 文本片段的字符數(shù) (L)
        // - interpolationCount 插值片段數(shù) (I)
        // 我們預(yù)計通常結(jié)構(gòu)會是像 "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) is cool]")
    }
    mutating func appendInterpolation(issue number: Int) {
        self.parts.append("[forever \(number) years old]")
    }
}
extension Person: ExpressibleByStringInterpolation {
    typealias StringInterpolation = StringInterpolationC
    init(stringInterpolation: StringInterpolationC) {
        let string = stringInterpolation.parts.joined()
        self.init(stringLiteral: string)
    }
}
func expressTest() {
    let person: Person = "Hello \(user: "Boat") and \(user: "xin")"
    let person2: Person = "Hello \(user: "Boat") and \(issue: 18)"
    print(person)
    print(person2)
//    let pperson =  PPerson(name: "xin weizhou", nickName: "zhou ge")
//    let str1: String = "Hello \(p: pperson)"
//    let str2: String = "Hello \(p: pperson, isFriend: true)"
//    print(str1)
//    print(str2)
}

案例二:擴展插值器功能,用以支持自定義差值類型性雄,本次案例擴展的是默認插值器DefaultStringInterpolation

Swift
struct PPerson {
    let name: String
    // 巴鐵的話一般叫昵稱就行了
    var nickName: String
    // 根據(jù)朋友關(guān)系窗慎,返回稱呼
    func getTitle(isFriend: Bool) -> String {
        isFriend ? nickName : name
    }
}
// 直接擴展DefaultStringInterpolation
extension DefaultStringInterpolation: StringInterpolationProtocol {
    mutating func appendInterpolation(p person: PPerson) {
        // 調(diào)用的 `appendLiteral(_ literal: String)` 接受 `String` 參數(shù)
        appendLiteral("\(person.name)")
    }
}
// 間接擴展DefaultStringInterpolation
extension String.StringInterpolation {
    mutating func appendInterpolation(p person: PPerson, isFriend: Bool) {
        appendLiteral(person.getTitle(isFriend: isFriend))
    }
}

遺留疑問米罚?

通過上面的例子可知僚碎,Person 會通過“插值器”拿到保存的數(shù)據(jù)挟憔;那么問題來了狸窘,案例二中String也會通過“默認插值器”(DefaultStringInterpolation)取得數(shù)據(jù)墩朦,但是我并沒有發(fā)現(xiàn)“默認插值器”有給外界提供數(shù)據(jù)的接口。另外我嘗試了在DefaultStringInterpolation擴展中添加description方法翻擒,對上面案例二的測試代碼結(jié)果并沒有影響氓涣。

Swift
extension DefaultStringInterpolation : CustomStringConvertible {
    /// A textual representation of this instance.
    ///
    /// Calling this property directly is discouraged. Instead, convert an
    /// instance of any type to a string by using the `String(describing:)`
    /// initializer. This initializer works with any type, and uses the custom
    /// `description` property for types that conform to
    /// `CustomStringConvertible`:
    ///     struct Point: CustomStringConvertible {
    ///         let x: Int, y: Int
    ///         var description: String {
    ///             return "(\(x), \(y))"
    ///         }
    ///     }
    ///     let p = Point(x: 21, y: 30)
    ///     let s = String(describing: p)
    ///     print(s)
    ///     // Prints "(21, 30)"
    ///
    /// The conversion of `p` to a string in the assignment to `s` uses the
    /// `Point` type's `description` property.
    @inlinable public var description: String { get }
}
extension DefaultStringInterpolation  {
     var description: String { return "Hello" }
}

附件

https://blog.csdn.net/weixin_34220963/article/details/91452976
https://developer.apple.com/documentation/swift/expressiblebystringinterpolation
https://onevcat.com/2021/03/swiftui-text-1/
http://www.reibang.com/p/7c2424be8b56

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市陋气,隨后出現(xiàn)的幾起案子劳吠,更是在濱河造成了極大的恐慌,老刑警劉巖巩趁,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痒玩,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機蠢古,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門奴曙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人草讶,你說我怎么就攤上這事洽糟。” “怎么了堕战?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵脊框,是天一觀的道長。 經(jīng)常有香客問我践啄,道長浇雹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任屿讽,我火速辦了婚禮昭灵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伐谈。我一直安慰自己烂完,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布诵棵。 她就那樣靜靜地躺著抠蚣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪履澳。 梳的紋絲不亂的頭發(fā)上嘶窄,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音距贷,去河邊找鬼柄冲。 笑死,一個胖子當著我的面吹牛忠蝗,可吹牛的內(nèi)容都是我干的现横。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼阁最,長吁一口氣:“原來是場噩夢啊……” “哼戒祠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起速种,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤姜盈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哟旗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贩据,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡栋操,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饱亮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矾芙。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖近上,靈堂內(nèi)的尸體忽然破棺而出剔宪,到底是詐尸還是另有隱情,我是刑警寧澤壹无,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布葱绒,位于F島的核電站,受9級特大地震影響斗锭,放射性物質(zhì)發(fā)生泄漏地淀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一岖是、第九天 我趴在偏房一處隱蔽的房頂上張望帮毁。 院中可真熱鬧,春花似錦豺撑、人聲如沸烈疚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爷肝。三九已至,卻和暖如春陆错,著一層夾襖步出監(jiān)牢的瞬間灯抛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工危号, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留牧愁,地道東北人素邪。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓外莲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兔朦。 傳聞我的和親對象是個殘疾皇子偷线,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容