美文网首页
StringInterpolation

StringInterpolation

作者: asaBoat | 来源:发表于2022-05-03 11:22 被阅读0次

字符串表达协议

ExpressibleByStringLiteral协议

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)
}
// 默认实现的 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协议

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)
}

// 默认实现的 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)
}

字符串相关

StringProtocol协议

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

摘掉和本文不相关的协议和约束,剩下

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 结构体

Swift
// String 实现协议 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 实现协议 ExpressibleByStringInterpolation 关联类型
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 实现协议 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
}

字符串插值协议与默认的插值器(暂且叫它)

StringInterpolationProtocol协议

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 结构体(插值器)

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
}

实例

案例一:实现自定义字符串插值器,通过插值器创建实例

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
  }
}
// 字符串插值器(来自网络)
struct StringInterpolationC: 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) 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
    // 根据朋友关系,返回称呼
    func getTitle(isFriend: Bool) -> String {
        isFriend ? nickName : name
    }
}
// 直接扩展DefaultStringInterpolation
extension DefaultStringInterpolation: StringInterpolationProtocol {
    mutating func appendInterpolation(p person: PPerson) {
        // 调用的 `appendLiteral(_ literal: String)` 接受 `String` 参数
        appendLiteral("\(person.name)")
    }
}
// 间接扩展DefaultStringInterpolation
extension String.StringInterpolation {
    mutating func appendInterpolation(p person: PPerson, isFriend: Bool) {
        appendLiteral(person.getTitle(isFriend: isFriend))
    }
}

遗留疑问?

通过上面的例子可知,Person 会通过“插值器”拿到保存的数据;那么问题来了,案例二中String也会通过“默认插值器”(DefaultStringInterpolation)取得数据,但是我并没有发现“默认插值器”有给外界提供数据的接口。另外我尝试了在DefaultStringInterpolation扩展中添加description方法,对上面案例二的测试代码结果并没有影响。

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/
https://www.jianshu.com/p/7c2424be8b56

相关文章

  • StringInterpolation

    字符串表达协议 ExpressibleByStringLiteral协议 ExpressibleByStringI...

  • Swift 5:理解字符串插值

    让我们深入探讨“字符串插值(StringInterpolation)”,一个 Swift 5 里很酷的特性。在“字...

  • Swift3.0-Study(一)

    Swift 用字符串插值(stringinterpolation)的方式把常量名或者变量名当做占位符加入到长字符串...

网友评论

      本文标题:StringInterpolation

      本文链接:https://www.haomeiwen.com/subject/ihbbyrtx.html