๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ“ ๊ธฐ๋ก/TIL

[TIL] 20231205

KVO(Key-value observing)

[๊ณต์‹ ๋ฌธ์„œ ๋ฒˆ์—ญ]

Key-value observing์€ ๋‹ค๋ฅธ ๊ฐœ์ฒด์˜ ์†์„ฑ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐœ์ฒด์—๊ฒŒ ์•Œ๋ฆด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Cocoa ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋ชจ๋ธ ๊ฐ„, ๋ทฐ ๊ฐ„ ๋“ฑ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋œ ์•ฑ์˜ ๋ถ€๋ถ„ ๊ฐ„์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. Key-value observing์€ ์˜ค์ง NSObject์—์„œ ์ƒ์†๋˜๋Š” ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ •๋ฆฌ

  • Key-value Observing์€ Cocoa ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจํ„ด
  • ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด ์‚ฌํ•ญ์„ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ์•Œ๋ฆฌ๋Š” ๋ฐ์— ์‚ฌ์šฉ๋จ
  • ๋ชจ๋ธ๊ณผ ๋ทฐ ์ฒ˜๋Ÿผ ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋Š” ๋ถ€๋ถ„์—์„œ์˜ ์ •๋ณด ์ „๋‹ฌ์— ์ ํ•ฉ
  • KVO๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ์ฒด๊ฐ€ NSObject๋ฅผ ์ƒ์†๋ฐ›์•„์•ผ ํ•จ

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

1. @objc dynamic var

key-value observing ์„ ํ†ตํ•ด ๊ด€์ฐฐํ•˜๊ณ ์ž ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ์— @objc ์–ดํŠธ๋ฆฌ๋ทฐํŠธ์™€ dynamic ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ KVO๋ฅผ ์‚ฌ์šฉํ•ด ๊ด€์ฐฐ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

 

class MyObjectToObserve: NSObject {
    @objc dynamic var myDate = NSDate(timeIntervalSince1970: 0) // 1970
    func updateDate() {
        myDate = myDate.addingTimeInterval(Double(2 << 30)) // Adds about 68 years.
    }
}

 

์œ„์˜ ์˜ˆ์‹œ ์ฝ”๋“œ์™€ ๊ฐ™์ด NSObject๋ฅผ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค ๋‚ด์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ @objc dynamic var๋กœ ์„ ์–ธํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

2. Observer ์ •์˜ - observe(_:options:changeHandler:) 

observer ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ”„๋กœํผํ‹ฐ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

observer๋ฅผ ์ •์˜ํ•  ๋•Œ๋Š” ๊ด€์ฐฐํ•˜๊ณ ์ž ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” key path๋ฅผ ๊ฐ€์ง„ observe(_:options:changeHandler:) ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ด€์ฐฐ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

class MyObserver: NSObject {
    @objc var objectToObserve: MyObjectToObserve
    var observation: NSKeyValueObservation?


    init(object: MyObjectToObserve) {
        objectToObserve = object
        super.init()


        observation = observe(
            \.objectToObserve.myDate,
            options: [.old, .new]
        ) { object, change in
            print("myDate changed from: \(change.oldValue!), updated to: \(change.newValue!)")
        }
    }
}

 

์œ„์˜ ์ฝ”๋“œ๋Š” NSKeyValueObservatedChange ์ธ์Šคํ„ด์Šค์˜ oldValue์™€ newValue๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€์ฐฐ ์ค‘์ธ ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•ด ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ์•Œ ํ•„์š”๊ฐ€ ์—†์œผ๋ฉด options ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ƒ๋žตํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. options ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ƒ๋žตํ•˜๋ฉด ์ƒˆ๋กœ์šด ํ”„๋กœํผํ‹ฐ ๊ฐ’๊ณผ ๋ณ€๊ฒฝ ์ „ ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด ์ €์žฅ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ oldValue์™€ newValue๊ฐ€ nil์ด ๋ฉ๋‹ˆ๋‹ค.

NotificationCenter

[๊ณต์‹ ๋ฌธ์„œ ๋ฒˆ์—ญ]

๋“ฑ๋ก๋œ ๊ด€์ฐฐ์ž(observers)์—๊ฒŒ ์ •๋ณด๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•  ์ˆ˜ ์žˆ๋Š” ์•Œ๋ฆผ ๋ฐœ์†ก ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.

 

๊ฐœ์ฒด๋Š” ์•Œ๋ฆผ ์„ผํ„ฐ์— ๋“ฑ๋กํ•˜์—ฌ addObserver(_:selector:name:object:using:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ addObserver(forName:object:queue:using:) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•Œ๋ฆผ(= NSNotification ๊ฐ์ฒด)์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ์ฒด๊ฐ€ ์ž์‹ ์„ ๊ด€์ฐฐ์ž๋กœ ์ถ”๊ฐ€ํ•  ๋•Œ ์–ด๋–ค ์•Œ๋ฆผ์„ ์ˆ˜์‹ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐœ์ฒด๋Š” ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์•Œ๋ฆผ์— ๋Œ€ํ•ด ๊ด€์ฐฐ์ž๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹คํ–‰ ์ค‘์ธ ๊ฐ ์•ฑ์—๋Š” default notification center๊ฐ€ ์žˆ์œผ๋ฉฐ, ํŠน์ • ์ปจํ…์ŠคํŠธ์—์„œ ํ†ต์‹ ์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด notification center๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•Œ๋ฆผ ์„ผํ„ฐ๋Š” ๋‹จ์ผ ํ”„๋กœ๊ทธ๋žจ ๋‚ด์—์„œ๋งŒ ์•Œ๋ฆผ์„ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์— ์•Œ๋ฆผ์„ ๋ฐœ์†ก, ์ˆ˜์‹ ํ•˜๋ ค๋ฉด DistributedNotificationCenter๋ฅผ ์‚ฌ์šฉ)

Post, Observer ๋ฉ”์„œ๋“œ

Post(์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š”) ๋ฉ”์„œ๋“œ

  • name: ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ์‹ ํ˜ธ ์ด๋ฆ„
  • object: ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์€ ๋ฐ์ดํ„ฐ. ์—†์œผ๋ฉด(์‹ ํ˜ธ๋งŒ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด) nil
  • userInfo: notification๊ณผ ๊ด€๋ จ๋œ ๊ฐ’ ๋˜๋Š” ๊ฐ์ฒด์˜ ์ €์žฅ์†Œ. ์—†์œผ๋ฉด nil

Observer(์•Œ๋ฆผ์„ ๋ฐ›๊ธฐ ์œ„ํ•œ ๊ด€์ฐฐ์ž) ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ ์ž์„ธํžˆ ๋ณด๊ธฐ!

  • observer: ๊ด€์ฐฐ์ž๋กœ ๋“ฑ๋กํ•  ๊ฐ์ฒด
  • selector: ํ•ด๋‹น ์‹ ํ˜ธ๋ฅผ ๋ฐ›์œผ๋ฉด ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  • name: observer๋ฅผ ๋“ฑ๋กํ•  notification ์ด๋ฆ„
  • object: observer๊ฐ€ ํ•ด๋‹น object์˜ notification๋งŒ ์ „๋‹ฌ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ ์ง€์ • ๊ฐ€๋Šฅ. nil์ผ ๊ฒฝ์šฐ๋Š” ํ•ด๋‹นํ•˜๋Š” ์‹ ํ˜ธ๋ฅผ ๋ชจ๋‘ ๋ฐ›๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
  • queue: block์ด ์‹คํ–‰๋˜๋Š” ์ž‘์—… ๋Œ€๊ธฐ์—ด (nil์ผ ๊ฒฝ์šฐ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰)ย˜
  • block: ์•Œ๋ฆผ์„ ๋ฐ›์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ๋ธ”๋ก

์˜ˆ์ œ ์ฝ”๋“œ

class ViewController: UIViewController {
    
    @IBOutlet weak var statusLabel: UILabel!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var messageTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func tapSubscribeButton(_ sender: Any) {
        NotificationCenter.default.addObserver(self, selector: #selector(dataReceived), name: NSNotification.Name("SendAlarm"), object: nil)
        statusLabel.text = "๊ตฌ๋…ํ•จ!!!!!"
    }
    
    @IBAction func tapSendButton(_ sender: Any) {
        NotificationCenter.default.post(name: NSNotification.Name("SendAlarm"), object: messageTextField.text ?? "")
    }
    
    @objc
    func dataReceived(notification: NSNotification) {
        if let text = notification.object as? String {
            titleLabel.text = text
        }
    }
}

 

'๐Ÿ“ ๊ธฐ๋ก > TIL' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[TIL] 20231207  (3) 2023.12.08
[TIL] 20231206  (0) 2023.12.08
[TIL] 20231204  (1) 2023.12.04
[TIL] 20231130  (2) 2023.11.30
[TIL] 20231129  (1) 2023.11.29