字符串表达协议
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