ロジック部分に全く手を入れることなし,例えば,デバッグ中に値の変更時のログ表示などができると便利かもしれません.個人的には,代入が絡むとケアレスミス連発で「また,やってしまった」とデバッグ時によく思うことがあります.しかし,デバッグログを後からあっちこっちに挟むのも結構面倒ですし,コードが見苦しくなったりします.何かいい手はないものでしょうか?
普通はwillSet, didSetは使うべき?
Swiftではクラス/構造体のプロパティでwillSet
とdidSet
記述すれば,値の更新前後になんらかのメッセージなりを発して値を監視することができます.しかし,この簡易監視機能は後からは追加できません.extensionはプロパティのオーバライドを禁じているからです.大元のクラス/構造体を変更する必要が出てきます.少々面倒な気がします.デバッグ目的にはあまり向いていない気がします.
代入演算子を定義してしまう
そこで,別解としてまず思いつくのは代入演算子のオーバーロードでしょうか.ですが残念なことに,言語仕様に
NOTE
The tokens =, ->, //, /*, */, ., and the prefix operator & are reserved. These >tokens can’t be overloaded, nor can they be used as custom operators.
とあり,デフォルトの代入演算子=
に手を入れることは叶いません.
言語の見た目が随分と変わってしまうのを許容できれば,別の代入演算子を定義してしまえばO.K.です.
Pascalの:=
も使えないなので,かなり変ですが,ここはとりあえず<-
を使うことにします.
では関数の定義です.
infix operator <- {
associativity none
precedence 130
}
func <-<T>(inout lhs: T, rhs: T) {
lhs = rhs
}
func <-<T:Debug>(inout lhs: T, rhs: T) {
println("Before: \(lhs)")
lhs = rhs
println("After: \(lhs)")
}
普通の代入とデバッグ用の代入関数(演算子)を定義してます.デバッグ用のは見ての通り,実際の代入の前後にログを出すようにしているだけです.簡単ですね.
ここではDebug
という制約を付けていますが,これは単なるマーカーで,
protocol Debug {}
この通り.このマーカーが付かないタイプは普通の代入が行われます.
デバッグしたくなったら,このマーカーを監視したいタイプに単にextensionします.例えば,次のようなストラクトで,
struct A: Printable {
var description: String {return "A(v=\(v))"}
let v: Int
}
extension A: Debug {} // Aにデバッグマーカーを設定
Debug
をくっ付けます.このextension有無だけでログの出力ができるようになります.
以下,デモとその出力です
var a = A(v: 1)
var b = A(v: 10)
a <- b // aにbを代入
println(a)
// Before: A(v=1)
// After: A(v=10)
// A(v=10)
var x: Int = 1
var y: Int = 10
x <- y
println(x)
// 10 // IntにはDebugを付けてない
と,まあこんな感じです.いかがでしょうか?(<-
は微妙...?)
まとめ
最初から代入演算子<-
を使ってさえいれば,メインロジックをいじらずに後からデバッグログをオン/オフできる.メインロジック中のあっちこっちに(邪魔な)ログ関数を挟む必要がかなり減るかもしれません.
(願望)
デフォルトの
=
もオーバーロードできるようにしてほしい.文法的には単なる値の代入以上の意味があるから厳しいか.extensionでのプロパティのオーバーライドを条件付きで認めてほしい.
willSet
とdidSet
の追加だけでも.値を変更しないのであれば別に問題はないと思います.