TCAのソースコードを眺めていた際にこのような書き方を見つけました。
fileprivate var path: PathView {
_read { yield PathView(base: self) }
_modify {
var path = PathView(base: self)
yield &path
self = path.base
}
set { self = newValue.base }
}
subscriptやcomputed propertyではget, setアクセッサが使用可能でしたが、_read, _modifyというものを見つけ詳しく調べてみることにしました。
調べた結果
どうやらgetやsetは以下のコードのようにコピーが発生してしまうらしく、パフォーマンスが重要な場面ではこれらのアクセッサは有用ではないとのこと。
var numbers = [1, 2, 3]
let hoge = numbers[2] * 2 // getから値がcopyされてくる
numbers[0] = hoge * hoge // setに渡す際に値がcopyされる
基本的な_readと_modifyの動作はgetとsetとほぼ同じみたいです。
var _x = 1
var x: Int {
_read { yield _x }
_modify { yield &_x }
}
print(x) // 1
x += 2
print(x) // 3
xに変更を加えると、_modifyが呼び出され、_xへのmutableな参照が取得されます。
yieldキーワードは呼び出し元へ処理を戻すために使用され、呼び出し側は取得した_xの参照で任意の変更を加えることができます(この例では+=を呼び出し変更を加えている)。
変更が終了したときに処理が_modifyに戻される、という流れです。
このように_modifyを使うことによって無駄なコピーを防ぎ、追加の記憶領域を使用することなく(in-place)値を変異させることができるようです。
めっちゃ難しいですね。
普段プログラムを書いててこの恩恵を受けることはほぼないと思いますが、標準ライブラリでは広く使われているらしく、Swiftを支える重要な概念となっています。
参考