字符串插值和字符串连接之间的区别

Cœu*_*œur 1 string string-concatenation string-interpolation swift swift3

当专门处理非可选String值时,字符串插值和字符串连接之间有什么区别?

struct MyModel {
    let value1: String
    let value2: String
    var displayNameByConcatenation: String {
        return value1 + "-" + value2
    }
    var displayNameByInterpolation: String {
        return "\(value1)-\(value2)"
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 是否会出现displayNameByConcatenationdisplayNameByInterpolation不同的情况?就像长 unicode 字符串一样?
  • 是否有可能以某种方式覆盖运算符的行为+或插值的行为以使它们在上面的示例中有所不同?
  • 一个比另一个更快/更慢吗?

请注意,从这个问题中我们了解到字符串插值将使用descriptionCustomStringConvertible 的 。但是String串联(运算符+)也调用吗description

Cœu*_*œur 7

从速度的角度来看,为了区分连接(value1 + "-" + value2)和插值("\(value1)-\(value2)"),结果可能取决于获得最终字符串所执行的操作次数。

我在 iPhone 8 上的结果表明:

  • 如果大约有 < 30 个子字符串连接在一起,则连接速度更快
  • 如果大约有 > 30 个子字符串要连接在一起,则插值速度更快

感谢塞壬们发现一个并不总是比另一个更快!

自己尝试一下(不要忘记根据您的需要调整测试的字符集和迭代):

import UIKit

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        DispatchQueue.global(qos: .default).async {
            ViewController.buildDataAndTest()
        }
    }

    private static func buildDataAndTest(times: Int = 1_000) {
        let characterSet = CharacterSet.alphanumerics
        characterSet.cacheAllCharacters()
        let data: [(String, String)] = (0 ..< times).map { _ in
             (characterSet.randomString(length: 50), characterSet.randomString(length: 20))
        }
        _ = testCIA(data)
        _ = testInterpol(data)
        print("concatenation: " + String(resultConcatenation))
        print("interpolation: \(resultInterpolation)")
    }

    /// concatenation in array
    static var resultConcatenation: CFTimeInterval = 0
    private static func testCIA(_ array: [(String, String)]) -> String {
        var foo = ""
        let start = CACurrentMediaTime()
        for (a, b) in array {
            foo = foo + " " + a + "+" + b
        }
        resultConcatenation = CACurrentMediaTime() - start
        return foo
    }

    /// interpolation
    static var resultInterpolation: CFTimeInterval = 0
    private static func testInterpol(_ array: [(String, String)]) -> String {
        var foo = ""
        let start = CACurrentMediaTime()
        for (a, b) in array {
            foo = "\(foo) \(a)+\(b)"
        }
        resultInterpolation = CACurrentMediaTime() - start
        return foo
    }
}

extension CharacterSet {
    static var cachedCharacters: [Character] = []

    public func cacheAllCharacters() {
        CharacterSet.cachedCharacters = characters()
    }

    /// extracting characters
    /// /sf/answers/3649355321/
    public func characters() -> [Character] {
        return codePoints().compactMap { UnicodeScalar($0) }.map { Character($0) }
    }
    public func codePoints() -> [Int] {
        var result: [Int] = []
        var plane = 0
        for (i, w) in bitmapRepresentation.enumerated() {
            let k = i % 8193
            if k == 8192 {
                plane = Int(w) << 13
                continue
            }
            let base = (plane + k) << 3
            for j in 0 ..< 8 where w & 1 << j != 0 {
                result.append(base + j)
            }
        }
        return result
    }

    // http://stackoverflow.com/a/42895178/1033581
    public func randomString(length: Int) -> String {
        let charArray = CharacterSet.cachedCharacters
        let charArrayCount = UInt32(charArray.count)
        var randomString = ""
        for _ in 0 ..< length {
            randomString += String(charArray[Int(arc4random_uniform(charArrayCount))])
        }
        return randomString
    }
}
Run Code Online (Sandbox Code Playgroud)