답변:
요점은 때로는 자동 저장 및 일부 동작 이있는 속성이 필요한 것 같습니다. 예를 들어 속성이 변경되었음을 다른 개체에 알립니다. 당신이 가지고있는 모든 경우 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)
... 예상되지 않습니다.
내 이해는 set과 get이 계산 된 속성을 위한 것입니다 ( 저장 된 속성 에서지지 않음 )
Objective-C에서 온 경우 명명 규칙이 변경되었음을 염두에 두어야합니다. Swift에서 iVar 또는 인스턴스 변수의 이름은 저장 속성입니다
var test : Int {
get {
return 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
}
}
}
비슷한 문제- 재귀 적으로 setter를 호출 하므로이 작업을 수행 할 수 없습니다 . 또한 초기화 할 저장된 속성이 없기 때문에이 코드는 이니셜 라이저에 대해 불평하지 않습니다 .
실제 저장된 속성의 조건부 설정을 허용하는 패턴은 다음과 같습니다.
//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
}
}
}
계산 및 저장된 속성을 모두 사용하십시오. 나는 계산 된 속성을 사용하여 같은 값을 두 번 설정하지 못하게합니다 (나쁜 일이 발생하지 않도록!); willSet 및 didSet을 사용하여 viewController에 알림을 전달했습니다 (viewController 컨테이너에 대한 UIViewController 설명서 및 정보 참조).
나는 이것이 도움이되기를 희망하며, 여기 어디에서나 실수를했다면 누군가가 소리 지르십시오!
//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.“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
for UITextField
객체 (예 :)의 고유 한 델리게이트 함수를 사용하는 것처럼 델리게이트 함수를 사용하여 모델의 주요 변경 사항을 관찰하기 위해 일부 뷰 컨트롤러에 사용할 수 있습니다 textFieldDidEndEditing(...)
.
이 간단한 예제 didSet
의 경우, 클래스 속성의 델리게이트 콜백을 사용하여 value
뷰 컨트롤러의 콘센트 중 하나에 연결된 모델 업데이트가 있음을 뷰 컨트롤러에 알립니다.
// 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()
무한 재귀.
Getter와 Setter는 때때로 적절한 값 변경을 관찰하기에는 구현하기에 너무 무겁습니다. 일반적으로 여기에는 추가 임시 변수 처리 및 추가 검사가 필요하며 수백 개의 게터와 세터를 작성하는 경우에도 이러한 작은 노력조차 피할 수 있습니다. 이런 것들이 상황에 대한 것입니다.
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
당신은 getter와 할 세터 싶습니다 무엇 이건 간단한 자리입니다. C #도 필요하지 않습니다. (컴파일러에 액세스하기 전에 요청한대로 구문 미묘한 부분을
get
&set
)은 기본적으로 다른 속성을 기반으로 속성을 계산 하는 것입니다 (예 : 레이블text
을 1 년으로 변환)Int
.didSet
&willSet
할 말이 있습니다 ...이 값이 설정되었습니다. 이제이 작업을 수행하십시오. 예를 들어 dataSource가 업데이트되었습니다 ... 테이블을 다시로드하여 새 행을 포함시킵니다. 다른 예를 들어 – 델리의 전화를 거는 방법에 대한 dfri의 답변을보십시오didSet