Swift에서 willSet 및 didSet의 목적은 무엇입니까?
Swift에는 C #과 매우 유사한 속성 선언 구문이 있습니다.
var foo: Int {
get { return getFoo() }
set { setFoo(newValue) }
}
그러나, 그것은 또한 가지고 willSet
와 didSet
행동. 이들은 각각 setter가 호출되기 전후에 호출됩니다. 세터 내에서 동일한 코드를 가질 수 있다고 생각하면 그 목적은 무엇입니까?
요점은 때로는 자동 저장 및 일부 동작 이있는 속성이 필요한 것 같습니다. 예를 들어 속성이 변경되었음을 다른 개체에 알립니다. 당신이 가지고있는 모든 경우 get
/ set
, 당신은 가치를 유지하기 위해 다른 필드가 필요합니다. 로 willSet
와 didSet
값이 다른 필드를 필요없이 수정 될 때, 당신은 조치를 취할 수 있습니다. 예를 들어,이 예에서 :
class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}
myProperty
수정 될 때마다 이전 값과 새 값을 인쇄합니다. getter와 setter를 사용하면 대신 이것이 필요합니다.
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 또는 인스턴스 변수의 이름은 저장 속성입니다
예제 1 (읽기 전용 속성)-경고 :
var test : Int {
get {
return test
}
}
재귀 함수 호출 (게터 호출 자체)이 발생하기 때문에 경고가 발생합니다.이 경우 경고는 "자체 게터 내에서 '테스트'를 수정하려고합니다"입니다.
예 2. 조건부 읽기 / 쓰기-경고
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를 호출 하므로이 작업을 수행 할 수 없습니다 . 또한 초기화 할 저장된 속성이 없기 때문에이 코드는 이니셜 라이저에 대해 불평하지 않습니다 .
예 3. 읽기 / 쓰기 계산 속성-백업 저장소
실제 저장된 속성의 조건부 설정을 허용하는 패턴은 다음과 같습니다.
//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 메소드를 사용해야 함).
예 4. will and did set 사용
//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을 볼 수 있습니다. 알림, 동기화 등을 보내는 데 유용합니다 (아래 예 참조).
예 5. 구체적인 예-ViewController 컨테이너
//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 설명서 및 정보 참조).
나는 이것이 도움이되기를 희망하며, 여기 어디에서나 실수를했다면 누군가가 소리 지르십시오!
이를 속성 관찰자 라고합니다 .
부동산 관찰자는 부동산 가치의 변화를 관찰하고 이에 대응합니다. 새 값이 속성의 현재 값과 동일하더라도 속성 값이 설정 될 때마다 속성 옵저버가 호출됩니다.
발췌 : Apple Inc.“Swift Programming Language.” iBooks. https://itun.es/ca/jEUH0.l
UI 요소와의 데이터 바인딩 또는 속성 변경으로 인한 부작용 발생, 동기화 프로세스 트리거, 백그라운드 처리 등과 같은 KVO로 전통적으로 할 일을 허용한다고 생각합니다 .
노트
willSet
및didSet
속성가 초기화에 설정되어있는 경우 위임이 발생하기 전에 관찰자가 호출되지 않습니다
를 사용하여 didSet
변수를 다른 값으로 설정할 수도 있습니다 . 이로 인해 속성 안내서에 명시된대로 관찰자가 다시 호출되지 않습니다 . 예를 들어 다음과 같이 값을 제한하려는 경우에 유용합니다.
let minValue = 1
var value = 1 {
didSet {
if value < minValue {
value = minValue
}
}
}
value = -10 // value is minValue now.
잘 쓰여진 기존의 많은 답변들이 그 질문을 잘 다루고 있지만, 내가 자세히 설명 할 가치가 있다고 생각되는 추가 사항을 언급 할 것입니다.
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()
무한 재귀.
속성에 새로운 값이 할당 될 때마다 속성에 대한 willSet 및 didSet 옵저버. 새 값이 현재 값과 동일한 경우에도 마찬가지입니다.
willSet
반면에 해결하려면 매개 변수 이름 이 필요 didSet
하지 않습니다.
속성 값이 업데이트 된 후 didSet 옵저버가 호출됩니다. 이전 값과 비교합니다. 총 단계 수가 증가한 경우 새 단계 수를 나타내는 메시지가 인쇄됩니다. didSet 옵저버는 이전 값에 대한 사용자 정의 매개 변수 이름을 제공하지 않으며 기본 이름 인 oldValue가 대신 사용됩니다.
Getter와 Setter는 때로는 적절한 값 변경을 관찰하기에는 구현하기에 너무 무겁습니다. 일반적으로 여기에는 추가 임시 변수 처리 및 추가 검사가 필요하며 수백 개의 게터와 세터를 작성하는 경우에도 이러한 작은 노력조차 피할 수 있습니다. 이런 것들이 상황에 대한 것입니다.
자신의 (기본) 클래스에서, willSet
그리고 원하는 사전 및 사후 처리에 액세스 하고 수행 하는 계산 된 속성 (예 : get 및 set 메소드)을 정의 할 수 didSet
있으므로 상당히 중복 됩니다 ._propertyVariable
, 경우 그러나 , 당신은 속성이되는 클래스 오버라이드 (override) 이미 정의를 , 다음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
제어로 바뀝니다.
참고 URL : https://stackoverflow.com/questions/24006234/what-is-the-purpose-of-willset-and-didset-in-swift
'IT story' 카테고리의 다른 글
파이썬 함수 전역 변수? (0) | 2020.04.04 |
---|---|
jQuery는 더 많은 매개 변수를 콜백에 전달 (0) | 2020.04.04 |
Node.js 웹 애플리케이션에서 MongoDB 연결을 어떻게 관리합니까? (0) | 2020.04.04 |
Java에서 사용자 홈 디렉토리를 찾는 가장 좋은 방법은 무엇입니까? (0) | 2020.04.04 |
SQL에서 TRUNCATE와 DELETE의 차이점은 무엇입니까? (0) | 2020.04.04 |