티스토리 뷰

iOS

[iOS] NotificationCenter 정리

Hani_Levenshtein 2021. 9. 10. 19:50

안녕하세요 Hani입니다.

이번에는 NotificationCenter에 대하여 알아볼 거예요. 🥰

 

 

먼저 MVC 구조를 한 번 살펴봅시당.

 

 

 

MVC 아키텍쳐 패턴에서 컨트롤러는 모델을 직접 참조하여 업데이트할 수 있지만 모델은 컨트롤러를 직접 참조하면 안 됩니다.

두 객체의 tightly coupled한 상태는 상호 의존성을 만들기 때문에

재사용성, 유연함에 악영향을 주기 때문이에요. 🥺

 

 

그럼 모델은 데이터 업데이트가 끝났다는 사실을 어떻게 컨트롤러에게 통지할 수 있을까요? 🧐

 

 

NotificationCenter는 모델과 컨트롤러 사이의 인터페이스 역할을 하여 두 객체를 loosely coupled하게 만들 수 있습니다.

 

NotificationCenter는 Foundation 프레임워크에 있는 클래스로,

포스팅된 Notification을 등록된 Observer들에게 퍼트려주는 역할을 해요.

 

NotificationCenter에 컨트롤러를 옵저버로 등록하고,

NotificationCenter에 모델이 Notification를 보내면

NotificationCenter는 해당 Notification을 원하는 옵저버를 찾아 전달해줍니다.  👍

 

 

 

tightly coupled한 관계에서의 모델-컨트롤러

1. 컨트롤러가 모델에 접근하여 데이터를 업데이트하도록 만듦

2. 업데이트 완료되었다고 모델이 컨트롤러에게 통지

3. 업데이트된 데이터를 컨트롤러가 가져감

 

 

loosely coupled한 관계에서의 모델-컨트롤러

1. 컨트롤러가 모델에 접근하여 데이터를 업데이트하도록 만듦

2. 모델이 NotificationCenter에 Notification을 Post

3. NotificationCenter가 Post된 Notification을 알맞은 옵저버에게 전달

 

 

 

이제 예시를 통해 한 번 알아보겠습니다. ☺️

 


class Poster {
    
    let notificationContent: String = "메롱"
    
    func postNotification() {
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: "NotificationPosted"),
            object: self,
            userInfo: ["content": self.notificationContent])
    }
}

먼저 Notification을 post할 클래스를 정의해봅시다.

 

NotificationCenter에 Notification을 post할 때는

Notification의 이름이 뭔지 

post하는 객체가 누군지

어떤 데이터를 보낼지

정할 수 있습니다.

 

object를 nil로 두면 Notification을 post한 객체를 밝히지 않을 수도 있는데

그렇게 해야 할 상황은 별로 없으니 post에서의 object는 self를 넣어두는 것이 좋겠습니다. 😎

 

class ViewController: UIViewController {

    lazy var poster: Poster = Poster()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(receiveNotification),
            name: Notification.Name.init("NotificationPosted"),
            object: poster)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        
        self.poster.postNotification()
    }
    
    @objc func receiveNotification(_ sender: Notification) {
        guard let content = sender.userInfo?["content"] as? String else { return }
        print(content)
    }

뷰컨에서는 NotificationCenter의 addObserver를 통해 옵저버를 등록했습니다.

 

NotificationCenter에 Observer를 추가할 때는

어떤 객체를 Observer로 등록할 건지

Notification을 받으면 어떤 함수를 실행할 건지

어떤 이름을 가진 Notification을 받을 건지

어떤 객체로부터만 Notification을 받을 건지

정할 수 있어요.

 

object를 nil로 설정해두면 name에서 설정한 Notification은 어떤 객체가 보내더라도 받을 수 있습니다.

post에서의 object와는 달리 객체를 특정하는 경우도 그렇지 않은 경우도 많습니다.

 

 

post를 통해 보낸 userInfo 데이터는 Any타입이기 때문에 자신이 사용할 타입으로 만들어야 해요. 

옵셔널 바인딩으로 처리해줍시다.

 

 

 

이번 예시에서는 뷰가 등장하면 poster의 메서드가 호출되도록 만들었어요. 

 

 

😝

 

 

 

addObserver도 있으니 removeObserver도 있습니다.

 

If your app targets iOS 9.0 and later or macOS 10.11 and later,
and you used addObserver(_:selector:name:object:) to create your observer,
you do not need to unregister the observer.
If you forget or are unable to remove the observer,
the system cleans up the next time it would have posted to it.

 

iOS 9.0부터는 옵저버를 삭제하지 않아도 다음에 시스템이 삭제해준다고 하지만

시스템이 삭제해주는 타이밍이 우리가 원하는 타이밍이 아닐 수도 있으니 필요에 따라

ViewDidLoad - Deinit 혹은

ViewWillAppear - ViewDidDisappear 처럼

뷰컨트롤러 생명주기에서 쌍을 이루는 메서드에 add와 remove를 명시하면 좋겠습니다. 😊

 

class ViewController: UIViewController {

    lazy var poster: Poster = Poster()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(receiveNotification),
            name: Notification.Name.init("NotificationPosted"),
            object: poster)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        
        self.poster.postNotification()
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(true)
        NotificationCenter.default.removeObserver(
            self,
            name: Notification.Name.init("NotificationPosted"),
            object: poster)
        
        // Or NotificationCenter.default.removeObserver(self)
    }
    
    @objc func receiveNotification(_ sender: Notification) {
        guard let content = sender.userInfo?["content"] as? String else { return }
        print(content)
    }
}

 


References

https://developer.apple.com/documentation/foundation/notificationcenter/

 

 

 

댓글