iOS
Swift

Swiftで文字を任意の数繰り返した文字列にしたいときはそれ用のメソッドがある

結論

Xcode8(Swift3ぐらい?)からStringに文字列を繰り返すメソッドがあったのでこれが使える

let s = String(repeating: "ab", count: 10)
print(s)
// Prints "abababababababababab"

Appleのリファレンス Documentation > Swift > String > init(repeating:count:)
https://developer.apple.com/documentation/swift/string/2427723-init

はじめに

文字列を規定以上に増やしてエラーになるような処理のテストコードを書きたかった。

rubyだと次のように書ける。

"a" * 2 # => "aa"

そのため、Swift 4.1でも同じようなことをやろうと、何も考えずにオペレータオーバーライドでのそれっぽい考えとして次のようなのがあると思う

import Foundation

func * (left: String, right: Int) -> String {
    var total = ""
    (0..<right).forEach { _ in
        total += left
    }
    return total
}

print("a" * 2)         // => "aa"
print(("a" * 2).count) // => 2

しかし、これだとStringオブジェクトをループごとに+=で作成してしまっていて無駄な気がする(例えば* 2000にすると2,000のStringが作られてる気がする)。テストで使いたい、無駄なコピーしてでも手に入れたいわけだけど、なんだか気になる。

ここで、Appleのリファレンスを調べているとString(repeating: "ab", count: 10)があることがわかった。

Swiftのコードは設計図共有サイトにあるので見てみる。まず、swift4.1のブランチ

https://github.com/apple/swift/blob/swift-4.1-branch/stdlib/public/core/StringLegacy.swift#L31-L44

  public init(repeating repeatedValue: String, count: Int) {
    if count == 0 {
      self = ""
      return
    }
    precondition(count > 0, "Negative count not allowed")
    let s = repeatedValue
    self = String(_storage: _StringBuffer(
        capacity: s._core.count * count,
        initialSize: 0,
        elementWidth: s._core.elementWidth))
    for _ in 0..<count {
      self += s
}

なるほどねー、たしかにcountが0のときとかマイナスのときの処理は必要だよねーと思ったものの、最後に結局+=で文字列を生成している様子。

ただ、swiftの最新2018-06-06のtag(swift-DEVELOPMENT-SNAPSHOT-2018-06-05-a)に切り替えてみると、ループで文字列を結合しているわけではないみたい🤔

https://github.com/apple/swift/blob/swift-DEVELOPMENT-SNAPSHOT-2018-06-05-a/stdlib/public/core/StringVariant.swift#L55-L65