728x90
KVO (Key-Value Observing)
- Key-Value Observing의 약자
- 객체의 특정 프로퍼티의 변경사항을 다른 객체에 알리기 위해 사용하는 코코아 프로그래밍 패턴
- Model가 View와 같이 논리적으로 분리된 파트 간의 변경사항을 전달하는데 유용하다.
- NSObject를 상속한 클래스에서만 KVO를 사용할 수 있다.
- didSet, willSet과 상당히 유사하다.
- 장점:
- 두 객체 간의 동기화가 가능
- 특정 객체의 내부구현을 바꾸지 않고 외부에서 프로퍼티의 변경을 관찰할 수 있다.
- KeyPath를 사용하여 nested 프로퍼티도 관찰이 가능하다.
- 따로 옵저버를 remove할 필요없이 시스템이 알아서 removeObserver를 호출한다.
- 단점:
- NSObject를 상속해야되기 때문에 Objective-C 런타임에 의존하게 된다.
다른 객체에서 observeValue 함수로 특정 프로퍼티를 observing하는 법
- Observed 되는 객체에 NSObject를 상속한 후 해당 프로퍼티에 @objc attribute와 dynamic modifier를 붙여준다.
- Observer 객체에 NSObject를 상속한 후 func observeValue(forKeyPath:) 메소드를 오버라이드한다.
import UIKit
import Foundation
class MyObject: NSObject {
@objc dynamic var observedValue: Int = 0
}
class MyObserver: NSObject {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "observedValue", let newValue = change?[.newKey] as? Int {
print("new \(newValue)")
}
}
}
let observer = MyObserver()
let object = MyObject()
object.addObserver(observer, forKeyPath: "observedValue", options: .new, context: nil)
object.observedValue = 3
/*
new 3
*/
changeHandler로 observe하는 법
- observe함수를 호출해서 keyPath, options, changeHandler를 지정해준다.
import UIKit
import Foundation
class MyObject: NSObject {
@objc dynamic var observedValue: Int = 0
}
let object = MyObject()
object.observe(\.observedValue, options: [.new, .old], changeHandler: { object, change in
print("old \(change.oldValue), new \(change.newValue)")
})
object.observedValue = 3
/*
old Optional(0), new Optional(3)
*/
options
- options에는 .new, .old, .initial, .prior의 4가지 값들이 있다.
- old, new는 말그대로 변경되기 전의 값, 변경된 후의 값을 가져온다는 것을 의미한다. (만약 이 옵션들을 지정하지 않으면 변경된 값들을 확인할 수가 없다. -> nil이 출력됨)
- initial은 초기값을 지정할 때도 observer가 호출되게 하고 싶을 때 추가하면 된다
- prior은 before, after 각각의 변화가 하나의 단일 notification이 아니라 분리된 notification으로 각각 observer로 보내진다.
willChange로서의 기능이 필요할 때 사용할 수 있다.
1. .initial
import UIKit
import Foundation
class MyObject: NSObject {
@objc dynamic var observedValue: Int = 0
}
let object = MyObject()
object.observe(\.observedValue, options: [.new, .old, .initial], changeHandler: { object, change in
print("old \(change.oldValue), new \(change.newValue)")
})
object.observedValue = 3
object.observedValue = 5
/*
old nil, new Optional(0)
old Optional(0), new Optional(3)
old Optional(3), new Optional(5)
*/
2. .prior
import UIKit
import Foundation
class MyObject: NSObject {
@objc dynamic var observedValue: Int = 0
}
let object = MyObject()
object.observe(\.observedValue, options: [.new, .old, .prior], changeHandler: { object, change in
print("old \(change.oldValue), new \(change.newValue)")
})
object.observedValue = 3
object.observedValue = 5
/*
old Optional(0), new nil
old Optional(0), new Optional(3)
old Optional(3), new nil
old Optional(3), new Optional(5)
*/
UIView의 center값을 observe하기
- redView가 움직일 때, center값을 추적해서 blueView도 같이 움직이게 한다.
class ViewController: UIViewController {
@IBOutlet weak var redView: DraggableView! //touch로 drag해서 움직일 수 있는 view (center값을 바꾼다)
var blueView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
let blueView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
blueView.backgroundColor = .blue
view.addSubview(blueView)
self.blueView = blueView
redView.addObserver(self, forKeyPath: "center", options: .new, context: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
blueView?.center = CGPoint(x: view.frame.width / 2, y: view.frame.height / 2)
}
}
extension ViewController {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "center", let center = change?[.newKey] as? CGPoint {
print("center \(center)")
blueView?.center = center
}
}
}
728x90
'iOS > 설명' 카테고리의 다른 글
[iOS] 레이아웃 update cycle (0) | 2022.04.18 |
---|---|
[iOS] autoresizingMask (0) | 2022.04.16 |
[iOS] APNs 이용해서 Push 보내기 (0) | 2022.04.09 |
[iOS] Real Device vs iOS Simulator (0) | 2022.04.09 |
[iOS] Lottie (0) | 2022.04.09 |
댓글