Swift には、C# と非常によく似たプロパティ宣言構文があります。
var foo: Int {
get { return getFoo() }
set { setFoo(newValue) }
}
ただし、アクションもありwillSet
ます。didSet
これらは、セッターが呼び出される前と後にそれぞれ呼び出されます。セッター内に同じコードを入れることができることを考えると、それらの目的は何ですか?
Swift には、C# と非常によく似たプロパティ宣言構文があります。
var foo: Int {
get { return getFoo() }
set { setFoo(newValue) }
}
ただし、アクションもありwillSet
ます。didSet
これらは、セッターが呼び出される前と後にそれぞれ呼び出されます。セッター内に同じコードを入れることができることを考えると、それらの目的は何ですか?
ポイントは、たとえば、プロパティが変更されたことを他のオブジェクトに通知するなど、自動ストレージといくつかの動作を持つプロパティが必要になる場合があることです。get
/しかない場合はset
、値を保持する別のフィールドが必要です。willSet
とを使用するとdidSet
、別のフィールドを必要とせずに値が変更されたときにアクションを実行できます。たとえば、その例では:
class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}
myProperty
変更されるたびに古い値と新しい値を出力します。ゲッターとセッターだけでは、代わりにこれが必要になります:
class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}
したがってwillSet
、didSet
数行の節約と、フィールド リストのノイズの減少を表します。
私の理解では、 set と get は計算されたプロパティ用です (格納されたプロパティからのバッキングはありません)
Objective-C から来ている場合は、命名規則が変更されたことを念頭に置いてください。Swift では、iVar またはインスタンス変数は格納されたプロパティと呼ばれます
var test : Int {
get {
return test
}
}
これにより、再帰的な関数呼び出しが発生するため、警告が発生します (getter 自体が呼び出されます)。この場合の警告は、「独自の getter 内で 'test' を変更しようとしています」です。
var test : Int {
get {
return test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
//(prevents same value being set)
if (aNewValue != test) {
test = aNewValue
}
}
}
同様の問題 -セッターを再帰的に呼び出しているため、これを行うことはできません。また、初期化する保存されたプロパティがないため、このコードは初期化子がないことについて文句を言わないことに注意してください。
これは、実際に保存されたプロパティの条件付き設定を可能にするパターンです
//True model data
var _test : Int = 0
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
注実際のデータは _test と呼ばれます (ただし、任意のデータまたはデータの組み合わせである可能性があります) _test は実際にはインスタンス変数であるため、初期値を提供する必要があることにも注意してください (または、init メソッドを使用する必要があります)
//True model data
var _test : Int = 0 {
//First this
willSet {
println("Old value is \(_test), new value is \(newValue)")
}
//value is set
//Finaly this
didSet {
println("Old value is \(oldValue), new value is \(_test)")
}
}
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
ここでは、willSet と didSet が実際に保存されているプロパティの変更をインターセプトしていることがわかります。これは、通知の送信、同期などに役立ちます... (以下の例を参照)
//Underlying instance variable (would ideally be private)
var _childVC : UIViewController? {
willSet {
//REMOVE OLD VC
println("Property will set")
if (_childVC != nil) {
_childVC!.willMoveToParentViewController(nil)
self.setOverrideTraitCollection(nil, forChildViewController: _childVC)
_childVC!.view.removeFromSuperview()
_childVC!.removeFromParentViewController()
}
if (newValue) {
self.addChildViewController(newValue)
}
}
//I can't see a way to 'stop' the value being set to the same controller - hence the computed property
didSet {
//ADD NEW VC
println("Property did set")
if (_childVC) {
// var views = NSDictionaryOfVariableBindings(self.view) .. NOT YET SUPPORTED (NSDictionary bridging not yet available)
//Add subviews + constraints
_childVC!.view.setTranslatesAutoresizingMaskIntoConstraints(false) //For now - until I add my own constraints
self.view.addSubview(_childVC!.view)
let views = ["view" : _childVC!.view] as NSMutableDictionary
let layoutOpts = NSLayoutFormatOptions(0)
let lc1 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
let lc2 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
self.view.addConstraints(lc1)
self.view.addConstraints(lc2)
//Forward messages to child
_childVC!.didMoveToParentViewController(self)
}
}
}
//Computed property - this is the property that must be used to prevent setting the same value twice
//unless there is another way of doing this?
var childVC : UIViewController? {
get {
return _childVC
}
set(suggestedVC) {
if (suggestedVC != _childVC) {
_childVC = suggestedVC
}
}
}
計算されたプロパティと保存されたプロパティの両方の使用に注意してください。同じ値が 2 回設定されるのを防ぐために、計算されたプロパティを使用しました (悪いことが起こらないようにするためです!)。私は willSet と didSet を使用して、viewControllers に通知を転送しました (UIViewController のドキュメントと viewController コンテナーに関する情報を参照してください)。
これがお役に立てば幸いです。ここで間違いを犯した場合は、誰かが叫んでください!
これらはプロパティ オブザーバーと呼ばれます。
プロパティ オブザーバーは、プロパティの値の変化を観察して対応します。プロパティ オブザーバーは、新しい値がプロパティの現在の値と同じであっても、プロパティの値が設定されるたびに呼び出されます。
抜粋: Apple Inc.「The Swift Programming Language」。アイブック。https://itun.es/ca/jEUH0.l
これは、UI 要素とのデータ バインディング、プロパティ変更の副作用のトリガー、同期プロセスのトリガー、バックグラウンド処理など、従来KVOで行っていたことを可能にするためだと思います。
ノート
willSet
didSet
委任が行われる前にプロパティが初期化子に設定されている場合、オブザーバーは呼び出されません
プロパティに新しい値が割り当てられるたびに、プロパティの willSet および didSet オブザーバー。これは、新しい値が現在の値と同じであっても当てはまります。
willSet
また、回避するにはパラメーター名が必要ですが、そうでdidSet
はないことに注意してください。
プロパティの値が更新された後、didSet オブザーバーが呼び出されます。古い値と比較します。ステップの総数が増えた場合は、新しいステップが何回実行されたかを示すメッセージが出力されます。didSet オブザーバーは、古い値のカスタム パラメーター名を提供せず、代わりに oldValue の既定の名前が使用されます。
私はC#を知りませんが、少し当て推量で理解できたと思います
foo : int {
get { return getFoo(); }
set { setFoo(newValue); }
}
します。Swift にあるものと非常によく似ていますが、同じではありません。Swift にはgetFoo
andがありませんsetFoo
。これは小さな違いではありません。つまり、値の基礎となるストレージがないことを意味します。
Swift には、格納され計算されたプロパティがあります。
計算されたプロパティはget
、持っている可能性がありますset
(書き込み可能な場合)。しかし、ゲッターとセッターのコードは、実際にデータを保存する必要がある場合、他のプロパティでそれを行う必要があります。バッキングストレージはありません。
一方、格納されたプロパティにはバッキング ストレージがあります。しかし、 と はありません。代わりに、変数の変更を観察し、最終的に副作用をトリガーしたり、保存された値を変更したりするために使用できます。計算されたプロパティにはとはありません。また、計算されたプロパティの場合はコードを使用して変更を制御できるため、それらは必要ありません。get
set
willSet
didSet
willSet
didSet
set