字符串表達協(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