ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Swift] Delegate, Notification, KVO
    Swift 2022. 3. 15. 08:50

    ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ์‹œ ํ”ํžˆ ๊ฒฝํ—˜ํ•˜๋Š” ๊ณตํ†ต์ ์ธ ๋ฌธ์ œ๋Š” ๊ณผ๋„ํ•œ ์ปคํ”Œ๋ง ์—†์ด ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ„์˜ ์†Œํ†ต์„ ํ—ˆ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. iOS์—์„œ ๋‚˜ํƒ€๋‚ด๋Š” 3๊ฐ€์ง€ ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

     

    1. Delegation
    2. Notifications through Notification Center
    3. Key Value Observing

     

    3๊ฐ€์ง€ ํŒจํ„ด์ด ๋‚˜์˜ค๊ฒŒ ๋œ ๋ฐฐ๊ฒฝ

    ๐Ÿ’ก ํ•œ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ ๋ฌถ์ด๊ธฐ (coupled)๋Š” ์‹ซ์ง€๋งŒ, ์†Œํ†ต์€ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ

     

    3๊ฐ€์ง€ ํŒจํ„ด์€ ๋ชจ๋‘ ํŠน์ • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์›ํ•˜๋Š” ๊ฐ์ฒด์— ์•Œ๋ ค์ฃผ๊ณ  ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค.

    ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŠน์„ฑ ์ƒ ๊ฐ์ฒด ๊ฐ„ ์†Œํ†ต์€ ํ•„์ˆ˜์ ์ด์ง€๋งŒ ํ•œ ๊ฐ์ฒด๋Š” ๊ทธ ์ž์ฒด๋กœ ์กด์žฌํ•˜๋ฉด์„œ ์†Œํ†ตํ•  ๋ฟ, ๋‹ค๋ฅธ ๊ฐ์ฒด์— ์ข…์†๋˜์–ด ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์€ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ๋…๋ฆฝ๋œ ๊ธฐ๋Šฅ์š”์†Œ ์ธก๋ฉด์—์„œ ์ข‹์ง€ ์•Š๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค๋ฉด ์šฐ๋ฆฌ๋Š” ํ•œ VC๊ฐ€ ๋‹ค๋ฅธ VC์™€ ๊ฒฐํ•ฉํ•˜์ง€ ์•Š๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ์กด์žฌํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž€๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Delegation, Notification, KVO๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

     

    1. Delegate

    • Delegate๋Š” Protocol์„ ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.
    • Protocol์€ Delegate๋กœ ์ง€์ •๋œ ๊ฐ์ฒด๊ฐ€ ์‚ฌ์šฉํ•  ๋ฉ”์†Œ๋“œ๋“ค์„ ๊ตฌํ˜„์€ ํ•˜์ง€ ์•Š๊ณ  ์ •์˜๋งŒ ํ•œ๋‹ค.
    • Delegate๋กœ ์ง€์ •๋œ ๊ฐ์ฒด๋Š” ์ด ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๊ณ , ํ”„๋กœํ† ์ฝœ์— ์ •์˜๋œ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
    • ๊ฐ์ฒด๋Š” ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ Delegate๋กœ ์ง€์ •๋œ ๊ฐ์ฒด์—๊ฒŒ ์•Œ๋ฆฌ๊ณ , Delegate๋กœ ์ง€์ •๋œ ๊ฐ์ฒด๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•œ๋‹ค. ๊ฐ์ฒด๋Š” ์ž์‹ ์˜ delegate๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค.
    • ์ฃผ๋กœ 1:1๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    // #1. ํ”„๋กœํ† ์ฝœ ์ •์˜
    protocol SideViewDelegate {
            func sideViewButtonDidTapped(_ button: UIButton)
    }
    
    class SideView: UIView {
            // #2. ๋‹ค๋ฅธ ๊ฐ์ฒด์—๊ฒŒ ๊ธฐ๋Šฅ์„ ์œ„์ž„ํ•˜๊ธฐ ์œ„ํ•œ delegate ํ”„๋กœํผํ‹ฐ
            var delegate: SideViewDelegate?
    
            @objc func sideViewButtonTapped(_ sender: UIButton) {
                    // #3. SideView์˜ Delegate๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋Š” delegate ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด sideViewButtonDidTapped ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค.
                    delegate?.sideViewButtonDidTapped(sender)        }
    }
    
    class ViewController: UIViewController {
    
            let sideView: SideView = {
                    let view = SideView()
                    view.translatesAutoresizingMaskIntoConstraints = false
            return view
            }()
    
            override func viewDidLoad() {
                    super.viewDidLoad()
    
                    // #6. sideView์˜ delegate ์—ญํ• ์„ ํ•˜๋Š” VC
                    sideView.delegate = self
            }
    }
    
    // #4. Delegate ์ฑ„์šฉ
    extension ViewController: SideViewDelegate {
            // #5. ํ”„๋กœํ† ์ฝœ์— ์ •์˜๋œ ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„
        func sideViewButtonDidTapped(_ button: UIButton) {
            print("SideView์˜ Button์ด Tapped๋˜์—ˆ์–ด์šฉ")
        }
    }

    ์žฅ์ 

    • ์—„๊ฒฉํ•œ Syntax๋กœ ์ธํ•ด ํ”„๋กœํ† ์ฝœ์— ํ•„์š”ํ•œ ๋ฉ”์†Œ๋“œ๋“ค์ด ๋ช…ํ™•ํžˆ ๋ช…์‹œ๋œ๋‹ค.
    • ์ปดํŒŒ์ผ ์‹œ ๊ฒฝ๊ณ /์—๋Ÿฌ๋กœ ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ํ”„๋กœํ† ์ฝœ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์•Œ๋ ค์ค€๋‹ค.
    • ํ”„๋กœํ† ์ฝœ์ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฒ”์œ„ ๋‚ด์—์„œ ์ •์˜๋œ๋‹ค.
    • ๋กœ์ง์˜ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๊ธฐ ์‰ฝ๋‹ค.
    • ํ”„๋กœํ† ์ฝœ ๋ฉ”์†Œ๋“œ๋กœ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์ •๋ณด๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๊ณผ์ •์„ ์œ ์ง€ํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์ œ 3์˜ ๊ฐ์ฒด (NotificationCenter ๊ฐ™์€ ์™ธ๋ถ€ ๊ฐ์ฒด)๊ฐ€ ํ•„์š” ์—†๋‹ค.
    • ํ”„๋กœํ† ์ฝœ์ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฒ”์œ„ ๋‚ด์—์„œ ์ •์˜๋œ๋‹ค.

    ๋‹จ์ 

    • ๋งŽ์€ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
    • ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— delegate ์„ค์ •์— nil์ด ๋“ค์–ด๊ฐ€์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผํ•œ๋‹ค.
    • ๋งŽ์€ ๊ฐ์ฒด๋“ค์—๊ฒŒ ์ด๋ฒคํŠธ๋ฅผ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์ด ์–ด๋ ต๊ณ  ๋น„ํšจ์œจ์ ์ด๋‹ค.
    • ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— delegate ํ”„๋กœํผํ‹ฐ๋ฅผ weak๋กœ ์„ ์–ธํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

     

    2. Notification

    • Notification Center๋ผ๋Š” ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ๋“ค์˜ ๋ฐœ์ƒ ์—ฌ๋ถ€๋ฅผ ์˜ต์ €๋ฒ„๋ฅผ ๋“ฑ๋กํ•œ ๊ฐ์ฒด๋“ค์—๊ฒŒ Notification์„ postํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
    • Notification์€ NSNotification ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๊ณ , Notification.Name์ด๋ผ๋Š” key ๊ฐ’์„ ํ†ตํ•ด ๋ณด๋‚ด๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
    • UserInfo ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ํ†ตํ•ด ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ฐ์ฒด๋“ค์ด ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ฉ”์‹œ์ง€์ด๋ฏ€๋กœ, ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์‹œ๊ฐ์ ์ธ ์•Œ๋ฆผ์„ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ํ—ˆ๊ฐ€ ์—†์ด ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (Authorization Not Required)
    • Notification์ด post๋˜๋ฉด ์ฆ‰์‹œ ํ•ด๋‹น Notification์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋“  Observer์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค. (Foreground, Background)
    • ์ฃผ๋กœ 1:N์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

    ํฐํŠธ๋ฅผ ์„ ํƒํ•˜๊ณ  ํฐํŠธ ์ •๋ณด๋ฅผ Notification์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” TableViewController

    Notification์„ ํ†ตํ•ด ์„ ํƒ๋œ ํฐํŠธ ์ •๋ณด๋ฅผ ๋ณด๋‚ธ๋‹ค.

    class SelectFontTableViewController: UITableViewController {
    
            func selectFont(theme: FontStyle) {
            if theme == .mapoFlowerIsland {
                Common.AppFontName.regular = "MapoFlowerIsland"
            } else if theme == .cafe24SurroundAir {
                Common.AppFontName.regular = "Cafe24SsurroundAir"
            } else if theme == .sdMisaeng {
                Common.AppFontName.regular = "SDMiSaeng"
            } 
    
                    // Notification
            DispatchQueue.global().async {
                NotificationCenter.default.post(name: NSNotification.Name.FontStyleDidChange, object: nil, userInfo: ["FontStyle": Common.AppFontName.regular])
            }
        }
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let fontStyle = Common.AppFontName.regular
    
            if fontStyle == "MapoFlowerIsland" {
                selectFont(theme: .mapoFlowerIsland)
            } else if fontStyle == "Cafe24SsurroundAir" {
                selectFont(theme: .cafe24SurroundAir)
            } else if fontStyle == "SDMiSaeng" {
                selectFont(theme: .sdMisaeng)
            }
        }
    
    
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            switch indexPath.row {
            case 0:
                selectFont(theme: .mapoFlowerIsland)
            case 1:
                selectFont(theme: .cafe24SurroundAir)
            default:
                selectFont(theme: .sdMisaeng)
            }
        }
    }

     

    ํฐํŠธ ์ •๋ณด Notification์„ ๋ฐ›๋Š” ViewController

    SelectFontViewController์—์„œ ํฐํŠธ๊ฐ€ ์„ ํƒ๋˜์–ด์„œ ๋ณด๋‚ด๋Š” Notification์„ FontPreviewController์—์„œ ๊ตฌ๋…ํ•œ๋‹ค.

    class FontPreviewViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            fontTextView.font = UIFont(name: Common.AppFontName.regular, size: 14)
    
                    // ์˜ต์ €๋ฒ„๋ฅผ ํ†ตํ•ด ๊ตฌ๋…ํ•œ๋‹ค.
            NotificationCenter.default.addObserver(self, selector: #selector(process(notification:)), name: Notification.Name.FontStyleDidChange, object: nil)
        }
    
            // ์˜ต์ €๋ฒ„ ํ•ด์ œ
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
    }

    ์žฅ์ 

    • ๋งŽ์€ ์ค„์˜ ์ฝ”๋“œ๊ฐ€ ํ•„์š” ์—†์ด ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋‹ค์ˆ˜์˜ ๊ฐ์ฒด๋“ค์—๊ฒŒ ๋™์‹œ์— ์ด๋ฒคํŠธ์˜ ๋ฐœ์ƒ์„ ์•Œ๋ ค์ค€๋‹ค.
    • Notification๊ณผ ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ Any? ํƒ€์ž…์˜ object, [AnyHashable: Any]? ํƒ€์ž…์˜ userInfo๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๋‹จ์ 

    • key ๊ฐ’์œผ๋กœ Notification์˜ ์ด๋ฆ„๊ณผ userInfo๋ฅผ ์„œ๋กœ ๋งž์ถ”๊ธฐ ๋•Œ๋ฌธ์— ์ปดํŒŒ์ผ ์‹œ ๊ตฌ๋…์ด ์ž˜ ๋˜๊ณ  ์žˆ๋Š”์ง€, ์˜ฌ๋ฐ”๋ฅด๊ฒŒ userInfo์˜ value๋ฅผ ๋ฐ›์•„์˜ค๋Š”์ง€ ์ฒดํฌํ•  ์ˆ˜ ์—†๋‹ค.
    • ์ถ”์ ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค.
    • Notification post ์ดํ›„ ์ •๋ณด๋ฅผ ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค.

     

    3. Key Value Observing (KVO)

    • A ๊ฐ์ฒด์—์„œ B ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€ํ™”๋จ์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ„ด
    • ์œ„์˜ ๋‘ ํŒจํ„ด์ด ์ฃผ๋กœ Controller์™€ ๋‹ค๋ฅธ ๊ฐ์ฒด ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ๋‹ค๋ฃฌ๋‹ค๋ฉด KVO ํŒจํ„ด์€ ๊ฐ์ฒด์™€ ๊ฐ์ฒด ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ๋‹ค๋ฃจ๋Š”๋ฐ ์ ํ•ฉํ•˜๋‹ค.
    • ๋ฉ”์†Œ๋“œ๋‚˜ ๋‹ค๋ฅธ ์•ก์…˜์—์„œ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ํ”„๋กœํผํ‹ฐ์˜ ์ƒํƒœ์— ๋ฐ˜์‘ํ•˜๋Š” ํ˜•ํƒœ์ด๋‹ค

    ์žฅ์ 

    • Model๊ณผ View์™€ ๊ฐ™์ด ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋œ ํŒŒํŠธ ๊ฐ„์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ „๋‹ฌํ•˜๋Š”๋ฐ ์œ ์šฉํ•˜๋‹ค.
    • ๋‘ ๊ฐ์ฒด ์‚ฌ์ด์˜ ์ •๋ณด๋ฅผ ๋งž์ถฐ์ฃผ๋Š” ๊ฒƒ์ด ์‰ฝ๋‹ค.
    • new/old value๋ฅผ ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
    • key path๋กœ observingํ•˜๊ธฐ ๋•Œ๋ฌธ์— nested objects ๋˜ํ•œ observingํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๋‹จ์ 

    • NSObject๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ฐ์ฒด์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
    • deallocํ•  ๋•Œ ์˜ต์ €๋ฒ„๋ฅผ ์ง€์›Œ์ค˜์•ผํ•œ๋‹ค.
    • ๋งŽ์€ value๋ฅผ ๊ฐ์ง€ํ•  ๋•Œ์—๋Š” ๋งŽ์€ ์กฐ๊ฑด๋ฌธ์ด ํ•„์š”ํ•˜๋‹ค.
    • objc dynamic ์†์„ฑ์„ ๋ถ™์—ฌ์ค˜์•ผํ•œ๋‹ค.

     

    ์ฐธ๊ณ  ๋ฌธ์„œ

    Delegation, Notification, ๊ทธ๋ฆฌ๊ณ  KVO

    When to use Delegation, Notification, or Observation in iOS - Shine Solutions Group

    ๋Œ“๊ธ€

Designed by Tistory.