[iOS] NotificationCenter

728x90

NotificationCenter에 대해 알아 보겠습니다.

 

NotificationCenter

NotificationCenter의 동작에 대해 먼저 이야기 해보겠습니다.

NotificationCenter에 등록된 Event가 발생하면 해당 Event들에 대한 행동을 취하는것.
앱 내에서 아무데서나 메시지를 던지면 앱 내의 아무데서나 이 메시지를 받을 수 있게 해 주는 것이 NSNotificationCenter의 역활.

 

NotificationCenter는 notification의 중계자 역할을 합니다.

//NotificationCenter Singleton Pattern
NotificationCenter.default

 

 

post

post는 전송 하는 notification입니다.
myNoti를 Observer에게 전달할 값을 전달해주는 부분 입니다.

NotificationCenter.default.post(name:"myNoti", object:" 전달할 값")

 

 

Observer 등록

옵저버란 말그대로 계속 탐지하는거라고 보시면 되는데, 뭘 탐지하느냐!
name이라는 키 값을 탐지하는겁니다.
어떤 객체가 "myNoti"라는 name으로 notification을 전송 했는데 옵저버에서 탐지 하고있다가 "myNoti"가 오면 수행 하는 녀석 입니다!

NotificationCenter.default.addObserver(observer:Any, selector:Selector, name: NSNotification.Name?, object:Any?)

//구현
NotificationCenter.default.addObserver(self, selector: #selector(handleNoti(_:)), name: myNoti, object: nil)

위의 소스를 보면, myNoti라는 name의 notification이 오면 selector를 실행하라 라는 뜻입니다.

@objc func handleNoti(_ noti: Notification) {
    print(noti)
    print("handleNoti")
}

selector의 handleNoti를 실행하면 파라미터로 post가 넘긴 name = myNoti, object = Optional(전달할 값), userInfo = nil 값이 넘어오게 됩니다.

 

Remove Observer

NotificationCenter 는 싱글턴 인스턴스라서 여러 오브젝트에서 공유합니다. 그래서 옵저버를 등록한 오브젝트가 메모리에서 해제되면 NSNotificationCenter에서도 옵저버를 없앴다고 알려줘야 됩니다.

//  self에 등록된 옵저버 전체 제거
notiCenter.removeObserver(self) 

//옵저버 하나 제거(등록된 특정 노티를 제거하기 위해 사용)
notiCenter.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)

보통은 Counterpart되는곳에 맞춰서 해제 시켜줍니다.

viewWillAppear < - > viewWillDisappear
viewdidload() < - > deinit() : 1번씩만 호출되니까

 

전체 코드 확인

 

ViewController.swift

class ViewController: UIViewController {

    let notiCenter = NotificationCenter.default
    
    var observer: NSObjectProtocol? // addObserver에 반환값이 있는 메서드를 사용할때 해제 해주어야 할 프로퍼티
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //object는 어떤객체를 전달할지 queue는 스레드 using은 수행할 로직.
        //NSNotification.Name.UIKeyboardWillShow 키보드가 나타나기 직전에 알려줘라
        // .을 사용하면 여러가지 설정이 있다.
        // 시스템에서 어떤 일이 일어났을때 캐치를 하고싶을때 컨트롤하고싶을때 notification을 사용해서 알 수 있다.
        notiCenter.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: .main) { (noti) in
            print(noti)
            print("UIKeyboardWillShow")
        }
        
        
        /***************************************
         # 노티피케이션을 이용해서 클로저로 반환을 하면은
         NSObjectProtocol라는 타입이 있기 때문에
         
         이런식으로 NSObjectProtocol를 해제해주어야된다.
         notiCenter.removeObserver(observer)
         ****************************************/
        observer = notiCenter.addObserver(forName: NSNotification.Name.UIApplicationDidBecomeActive, object: nil, queue: .main) { (noti) in
            print(noti)
            print("UIApplicationDidBecomeActive")
        }

        //Device가 회전했는지 체크
        //notiCenter.addObserver(forName: NSNotification.Name.UIDeviceOrientationDidChange, object: nil, queue: .main) { (noti) in
            //print(noti)
            //print("UIDeviceOrientationDidChange")
        //}
        
        
        notiCenter.addObserver(self, selector: #selector(deviceOrientationDidChange(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
        //똑같은 Notification이 등록되면 중복되서 날라간다.
        //notiCenter.addObserver(self, selector: #selector(deviceOrientationDidChange(_:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
        
        
    }

    @objc func deviceOrientationDidChange(_ noti: Notification) {
        print(noti)
        print("deviceOrientationDidChange")
    }
    
    // 매칭되는곳에 맞춰서 해제 시켜줘야된다.viewWillAppear <-> viewWillDisappear
    // viewdidload() <-> deinit() : 1번씩만 호출되니까
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    }
    
    //NotificatioCenter는 싱글턴이여서 메모리가 계속 상주하기 때문에 해제를 시켜줘야된다.
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        notiCenter.removeObserver(self) //  self에 등록된 옵저버 전체 제거
        notiCenter.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil) //옵저버 하나 제거(등록된 특정 노티를 제거하기 위해 사용)
    }
    
    @IBAction func postButton(_ sender: UIButton) {
        
        //AnotherVC에 있는 myNoti에 송출하기 위해 사용.
        NotificationCenter.default.post(name: myNoti, object: "김승진")
    }
    
    
    deinit {
        print("deinit")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}


AnotherViewController.swift

import UIKit

let myNoti = Notification.Name.init("myNoti") // 외부에서 접근 가능하도록 하기위해 클래스 위에 선언한것.

//NotiName을 설정하는 방법의 또다른 방식.
extension Notification.Name {
    static let myNoti2 = Notification.Name.init("kMyNoti2")
}

class AnotherViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(handleNoti(_:)), name: myNoti, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(handleNoti(_:)), name: Notification.Name.myNoti2, object: nil)
    }

    //myNoti라 Name이라는 것을 받으면 작동하는 메서드
    @objc func handleNoti(_ noti: Notification) {
        print(noti)
        print("handleNoti")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}