美文网首页
Swift 5.0中超强大的字符串插值

Swift 5.0中超强大的字符串插值

作者: 星星星宇 | 来源:发表于2020-09-11 13:30 被阅读0次

    基础

    我们已经习惯了像这样的字符串插值

    let age = 38
    print("You are \(age)")
    

    与以前相比,语法已经极大的改进,
    以前是这样的

    [NSString stringWithFormat:@"%ld", (long)unreadCount];
    

    性能也改进了,
    以前可能是这样的

    let all = s1 + s2 + s3 + s4 // Swift 需要s1+s2得到s5,s5+s3得到s6,s6+s4得到s7,最后分配到all。
    

    字符串插值变化自Swift2.1,可以这样使用

    print("Hi, \(user ?? "Anonymous")")
    

    现在Swift Evolution 不断推送Swift向前发展,Swift4.2中就引入了很多功能。
    Swift5.0中,ABI稳定性是重点,字符串插值也获得了超级功能,我们可以更好的控制它。
    Swift5.0中新的字符串插值系统,我们可以扩展String.StringInterpolation来添加我们的自定义插值

    // 覆盖了协议方法
    extension String.StringInterpolation {
        mutating func appendInterpolation(_ value: Int) {
            let formatter = NumberFormatter()
            formatter.numberStyle = .spellOut
            if let result = formatter.string(from: value as NSNumber) {
                appendLiteral(result)
            }
        }
    }
    
    let age = 18
    print("I'm \(age)")
    

    这时将打印 I'm eighteen。

    为了避免混淆你的小伙伴,你不应该覆盖Swift的默认值,因此需要命名参数来避免混淆。

    // 通过添加命名参数来区分默认的方法
    mutating func appendInterpolation(format value: Int) {
    

    参数插补

    在方法上添加第二个type参数

    mutating func appendInterpolation(format value: Int, using style: NumberFormatter.Style) {
        let formatter = NumberFormatter()
        formatter.numberStyle = style
    
        if let result = formatter.string(from: value as NSNumber) {
            appendLiteral(result)
        }
    }
    

    使用它

    print("Hi, I'm \(format: age, using: .spellOut).")
    

    参数也可以是其它类型。
    Erica Sadun 给出的示例。

    extension String.StringInterpolation {
        // autoclosure 自动闭包,适用于()->T这样的无参闭包
        mutating func appendInterpolation(if condition: @autoclosure () -> Bool, _ literal: StringLiteralType) {
            guard condition() else { return }
            appendLiteral(literal)
        }
    }
    

    使用它

    let doesSwiftRock = true
    print("Swift rocks: \(if: doesSwiftRock, "(*)")")
    

    为自定义类型添加插值

    struct Person {
        var type: String
        var action: String
    }
    
    extension String.StringInterpolation {
        mutating func appendInterpolation(_ person: Person) {
            appendLiteral("I'm a \(person.type) and I'm gonna \(person.action).")
        }
    }
    
    let hater = Person(type: "hater", action: "hate")
    print("Status check: \(hater)")
    

    使用字符串插值的好处是不会触碰对象的调试描述。

    print(hater)
    

    print还是原始数据

    下面是一个接收泛型对象参数的自定义插值函数

    mutating func appendInterpolation<T: Encodable>(debug value: T) {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
    
        if let result = try? encoder.encode(value) {
            let str = String(decoding: result, as: UTF8.self)
            appendLiteral(str)
        }
    }
    

    带插值的构建类型

    如上所述,插值是一种非常干净的控制数据格式化的方式。
    我们也可以通过插值来构建自己的类型。

    我们将使用字符串插值方法来创建带颜色的字符串的类型。

    struct ColorString: ExpressibleByStringInterpolation {
        
        // 嵌套结构,插入带属性的字符串
        struct StringInterpolation: StringInterpolationProtocol {
    
            // 存储带属性字符串
            var output = NSMutableAttributedString()
            // 默认的字符串属性
            var baseAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Georgia-Italic", size: 64) ?? .systemFont(ofSize: 64), .foregroundColor: UIColor.black]
            // 必须的,创建时可用于优化性能
            init(literalCapacity: Int, interpolationCount: Int) {}
    
            // 添加默认文字时
            mutating func appendLiteral(_ literal: String) {
                let attributedString = NSAttributedString(string: literal, attributes: baseAttributes)
                output.append(attributedString)
            }
            
            // 添加带颜色的文字时
            mutating func appendInterpolation(message: String, color: UIColor) {
                var colorAtt = baseAttributes
                colorAtt[.foregroundColor] = color
                let attStr = NSAttributedString(string: message, attributes: colorAtt)
                output.append(attStr)
            }
        }
        
        // 所有文字处理完成后,存储最终的文字
        let value: NSAttributedString
        // 从普通字符串初始化
        init(stringLiteral value: StringLiteralType) {
            self.value = NSAttributedString(string: value)
        }
        
        // 从带颜色的字符串初始化
        init(stringInterpolation: StringInterpolation) {
            self.value = stringInterpolation.output
        }
    }
    
    
    let str: ColorString = "asdfd\(message: "Red", color: .red)\(message: "Red", color: .red)\(message: "Blue", color: .blue)"
    print(str.value)
    
    var interploation = ColorString.StringInterpolation(literalCapacity: 1, interpolationCount: 1)
    interploation.appendLiteral("111111")
    interploation.appendInterpolation(message: "abc", color: .red)
    interploation.appendLiteral("123")
    let valentine = ColorString(stringInterpolation: interploation)
    let valentine1 = ColorString(stringLiteral: "abc")
    print(valentine.value)
    print(valentine1.value.string)
    

    不使用语法糖,也可以完成字符串的创建。

    总结

    自定义字符串插值,可以将格式代码封装在某个地方,使代码更简洁。同时,我们还可以创建一种原生的构件类型。
    不过,这只是其中一种方式,你可以用插值法,也可以用函数,甚至其它方式。这需要根据具体情况来选择。

    相关文章

      网友评论

          本文标题:Swift 5.0中超强大的字符串插值

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