初期化コンテキストの外部でdidSet()
関数を呼び出さずに、Swiftでプロパティの値を設定するにはどうすればよいですか?以下のコードは、クラスのnoside()
関数内でこれを達成するための失敗した実験でした。
class Test
{
var toggle : Bool = 0
var hoodwink : Int = 0 {
didSet(hoodwink)
{
toggle = !toggle
}
}
// failed attempt to set without a side effect
func noside(newValue : Int)
{
hoodwink = newValue
println("hoodwink: \(hoodwink) state: \(toggle)")
}
func withside(newValue : Int)
{
self.hoodwink = newValue
println("hoodwink: \(hoodwink) state: \(toggle)")
}
}
自動合成されたプロパティを使用してObjective-Cで行うのは非常に簡単です。
副作用あり(セッターに存在する場合):
self.hoodwink = newValue;
副作用なし:
_hoodwink = newValue;
「副作用を回避する」ためにObjective-Cで行うことは、プロパティのバッキングストア(デフォルトではアンダースコアがプレフィックスとして付けられているインスタンス変数)にアクセスすることです(これは@synthesize
ディレクティブを使用して変更できます)。
ただし、Swift言語設計者は、プロパティのバッキング変数にアクセスできないように特別な注意を払ったようです。本によると、
Objective-Cの経験がある場合は、クラスインスタンスの一部として値と参照を格納する2つの方法を提供していることをご存知かもしれません。プロパティに加えて、プロパティに格納されている値のバッキングストアとしてインスタンス変数を使用できます。
Swiftは、これらの概念を単一のプロパティ宣言に統合します。 A Swiftプロパティには対応するインスタンス変数がなく、プロパティのバッキングストアに直接アクセスされません。(強調は私のものです)
もちろん、これは、リフレクションを使用するのではなく、「正規言語」手段を使用する場合にのみ適用されます。読みやすさを犠牲にして、この制限を回避する方法を提供する可能性があります。
これを回避するためのハックの可能性は、didSetをバイパスするセッターを提供することです。
var dontTriggerObservers:Bool = false
var selectedIndexPath:NSIndexPath? {
didSet {
if(dontTriggerObservers == false){
//blah blah things to do
}
}
}
var primitiveSetSelectedIndexPath:NSIndexPath? {
didSet(indexPath) {
dontTriggerObservers = true
selectedIndexPath = indexPath
dontTriggerObservers = false
}
}
醜いが実行可能
いつ副作用を適用したいかを正確に知っている場合は、明示的にそれを作成してください。
1つの解決策:
func noside(newValue : Int) {
hoodwink = newValue
}
func withside(newValue : Int) {
self.hoodwink = newValue
toggle = !toggle
}
2解決策:
var toggle : Bool = false
var hoodwink : Int = 0
var hoodwinkToggle: Int {
get { return hoodwink }
set(newValue) {
hoodwink = newValue
toggle = !toggle
}
}
これらの解決策はより明確で読みやすいと思います。1つの変数を使用すると、ある呼び出しでは副作用が発生し、他の呼び出しでは発生しないはずです。
私が解決しようとしていた問題は、1つの値が変更されたときに、または両方が同時に変更された場合に1つ、doCommonUpdate
呼び出しを取得したかったことです。これを行うと再帰が発生します。どちらかの値が変更されると、毎回didSet
が呼び出されるためです。セットアップの例、私のものはもっと複雑でした:
class Person {
var first: String = "" {
didSet {
updateValues(first: first, last: last)
}
}
var last: String = "" {
didSet {
updateValues(first: first, last: last)
}
}
init() {}
init(first: String, last: String) {
self.first = first
self.last = last
}
// also want to set both at the same time.
func updateValues(first: String, last: String) {
// self.first = first // causes recursion.
// self.last = last
doCommonSetup(first: first, last: last)
}
func doCommonSetup(first: String, last: String) {
print(first, last)
}
}
let l = Person()
l.first = "Betty"
l.last = "Rubble"
_ = Person(first: "Wilma", last: "Flintstone")
> Betty
> Betty Rubble
コメントされた行があるため、Wilmaは印刷しません。
私の解決策は、これらすべての変数を別の構造体に移動することでした。このアプローチは問題を解決し、意味を助ける別のグループを作成するという副次的な利点があります。どちらかの値が個別に変更された場合、および両方の値が同時に変更された場合でも、doCommonSetup
を取得します。
class Person2 {
struct Name {
let first: String
let last: String
}
var name: Name
init() {
name = Name(first: "", last: "")
}
init(first: String, last: String) {
name = Name(first: first, last: last)
updateValues(name: name)
}
var first: String = "" {
didSet {
name = Name(first: first, last: self.last)
updateValues(name: name)
}
}
var last: String = "" {
didSet {
name = Name(first: self.first, last: last)
updateValues(name: name)
}
}
// also want to set both at the same time.
func updateValues(name: Name) {
self.name = name
doCommonSetup(name: name)
}
func doCommonSetup(name: Name) {
print(name.first, name.last)
}
}
let p = Person2()
p.first = "Barney"
p.last = "Rubble"
_ = Person2(first: "Fred", last: "Flintstone")
> Barney
> Barney Rubble
> Fred Flintstone