美文网首页
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

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