回答:
重要なのは、時々、たとえば、プロパティが変更されたことを他のオブジェクトに通知するために、自動ストレージといくつかの動作を備えたプロパティが必要になることです。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
数行の経済性を表し、フィールドリストのノイズを減らします。
willSet
そしてdidSet
あなたはアップルのノートとしてinitメソッド内からプロパティを設定するときに呼び出されていない:willSet and didSet observers are not called when a property is first initialized. They are only called when the property’s value is set outside of an initialization context.
myArrayProperty.removeAtIndex(myIndex)
。...予期されていません。
私の理解では、設定と取得は計算されたプロパティ用です(保存されたプロパティからのバッキングはありません)
Objective-Cを使用している場合は、命名規則が変更されたことを念頭に置いてください。Swiftでは、iVarまたはインスタンス変数はstoredプロパティと呼ばれます
var test : Int {
get {
return test
}
}
これにより、再帰的な関数呼び出し(ゲッター自体が呼び出される)になるため、警告が表示されます。この場合の警告は、「独自のゲッター内で「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コンテナの情報を参照してください)。
これがお役に立てば幸いです。ここで間違いを犯した場合は、誰かが叫んでください!
//I can't see a way to 'stop' the value being set to the same controller - hence the computed property
if let newViewController = _childVC {
代わり に私が使用した後の警告が消える if (_childVC) {
get
、if _childVC == nil { _childVC = something }
次にを追加する必要があると思いますreturn _childVC
。
これらはプロパティオブザーバーと呼ばれます。
プロパティオブザーバーは、プロパティの値の変化を観察して応答します。プロパティオブザーバーは、新しい値がプロパティの現在の値と同じであっても、プロパティの値が設定されるたびに呼び出されます。
抜粋:Apple Inc.「The Swift Programming Language」iBooks。https://itun.es/ca/jEUH0.l
UI要素とのデータバインディング、プロパティ変更の副作用のトリガー、同期プロセスのトリガー、バックグラウンド処理など、KVOで従来から行っていたことが可能になると思います。
注意
willSet
そして、didSet
プロパティが初期化に設定されている場合、委任が行われる前にオブザーバーは呼び出されません
よく書かれた多くの既存の回答がこの問題を十分にカバーしていますが、カバーする価値があると私が信じている追加について少し詳しく述べます。
財産オブザーバーは今までユーザーとの対話によって更新されているクラスのプロパティのために、例えば、デリゲートを呼び出すために使用することができますが、オブジェクトの初期化時にデリゲートを呼び出さないようにしたいところ。willSet
didSet
承認された回答に対するKlaasの投票コメントを引用します。
プロパティが最初に初期化されるとき、willSetおよびdidSetオブザーバーは呼び出されません。プロパティの値が初期化コンテキスト外で設定されている場合にのみ呼び出されます。
これは、たとえば、didSet
プロパティが、独自のカスタムクラスのデリゲートコールバックと関数の起動ポイントの適切な選択であることを意味するため、非常に優れています。
例として、いくつかのキープロパティvalue
(たとえば、評価コントロール内の位置)を持ち、のサブクラスとして実装されたカスタムユーザーコントロールオブジェクトを考えますUIView
。
// CustomUserControl.swift
protocol CustomUserControlDelegate {
func didChangeValue(value: Int)
// func didChangeValue(newValue: Int, oldValue: Int)
// func didChangeValue(customUserControl: CustomUserControl)
// ... other more sophisticated delegate functions
}
class CustomUserControl: UIView {
// Properties
// ...
private var value = 0 {
didSet {
// Possibly do something ...
// Call delegate.
delegate?.didChangeValue(value)
// delegate?.didChangeValue(value, oldValue: oldValue)
// delegate?.didChangeValue(self)
}
}
var delegate: CustomUserControlDelegate?
// Initialization
required init?(...) {
// Initialise something ...
// E.g. 'value = 1' would not call didSet at this point
}
// ... some methods/actions associated with your user control.
}
これは後にデリゲート関数は、のためのモデルの重要な変化を観察するために、いくつかのビューコントローラ、たとえば、で使用することができCustomViewController
ますが、本来の委任機能を使用すると思いずっと同じように、UITextFieldDelegate
のためにUITextField
オブジェクト(例えばtextFieldDidEndEditing(...)
)。
この簡単な例でdidSet
は、クラスプロパティのからのデリゲートコールバックを使用して、value
そのアウトレットの1つにモデルの更新が関連付けられていることをビューコントローラに通知します。
// ViewController.swift
Import UIKit
// ...
class ViewController: UIViewController, CustomUserControlDelegate {
// Properties
// ...
@IBOutlet weak var customUserControl: CustomUserControl!
override func viewDidLoad() {
super.viewDidLoad()
// ...
// Custom user control, handle through delegate callbacks.
customUserControl = self
}
// ...
// CustomUserControlDelegate
func didChangeValue(value: Int) {
// do some stuff with 'value' ...
}
// func didChangeValue(newValue: Int, oldValue: Int) {
// do some stuff with new as well as old 'value' ...
// custom transitions? :)
//}
//func didChangeValue(customUserControl: CustomUserControl) {
// // Do more advanced stuff ...
//}
}
ここでは、value
プロパティがカプセル化されていますが、一般に、このような状況では、ビューコントローラーの関連するデリゲート関数(ここでは)のスコープでオブジェクトのvalue
プロパティを更新しないように注意してください。更新しないと、無限再帰。customUserControl
didChangeValue()
ゲッターとセッターは、適切な値の変化を観察するためだけに実装するには重すぎる場合があります。通常、これには追加の一時変数処理と追加のチェックが必要であり、何百ものゲッターとセッターを作成する場合は、これらの小さな労力でさえ回避する必要があります。これらは状況に応じたものです。
willSet
didSet
一つのことdidSet
あなたは追加の設定を追加するためにコンセントを使用するときに本当に便利ですです。
@IBOutlet weak var loginOrSignupButton: UIButton! {
didSet {
let title = NSLocalizedString("signup_required_button")
loginOrSignupButton.setTitle(title, for: .normal)
loginOrSignupButton.setTitle(title, for: .highlighted)
}
C#はわかりませんが、少し推測するだけで、
foo : int {
get { return getFoo(); }
set { setFoo(newValue); }
}
します。Swiftにあるものとよく似ていますが、同じではありません。SwiftにはgetFoo
and がありませんsetFoo
。それは小さな違いではありません。つまり、価値のための基盤となるストレージがないということです。
Swiftはプロパティを保存および計算しました。
計算されたプロパティには(書き込み可能であれば)ありget
、ある場合がありますset
。ただし、ゲッターとセッターのコードは、実際にデータを格納する必要がある場合は、他のプロパティで実行する必要があります。バッキングストレージはありません。
一方、保存されたプロパティにはバッキングストレージがあります。しかし、それはないではない持っているget
とset
。代わりに、それは持っているwillSet
とdidSet
あなたは変数の変化を観察して、最終的には、トリガーの副作用を、および/または格納された値を変更するために使用できます。計算されたプロパティはwillSet
ありません。またdidSet
、計算されたプロパティではコードを使用してset
変更を制御できるため、それらは必要ありません。
getFoo
そしてsetFoo
、ゲッターとセッターに何をしてほしいのかという単純なプレースホルダーです。C#でも必要ありません。(コンパイラーにアクセスする前に尋ねたように、私はいくつかの構文上の微妙な部分を逃しました。)
get
&set
)は、基本的に、別のプロパティに基づいて計算されたプロパティを持つことです。たとえば、ラベルをtext
年に変換しInt
ます。didSet
&willSet
言うことはあります...この値が設定されたので、これを実行しましょう。たとえば、dataSourceが更新されたので、tableViewをリロードして新しい行を含めます。別の例については、代理人を呼び出す方法に関するdfriの回答をdidSet