티스토리 뷰

이번주 포스트의 주제는 NotificationCenter입니다.

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

 

Apple Developer Documentation

 

developer.apple.com

 

A가 B에게 카톡을 보내면 B의 폰에 알림이 옵니다!

A가 B에게 카톡을 "보내면" B의 폰에 "알림"이 옵니다.

 

어떠한 이벤트에 대해 구독하고, 해당 이벤트가 일어나면 알림을 주는 역할이 NotificationCenter입니다.

NotificationCenter는 URLSession, UserDefault와 같이 default를 통해 싱글톤을 사용할 수 있습니다.

 

 

NotificaiionCenter.default.post(name: <#T##NSNotification.Name#>, object: <#T##Any?#>, userInfo: <#T##[AnyHashable : Any]?#>)

post를 사용하여 이벤트에 대한 알림을 보낼 수 있습니다.

name에는 어떤 이벤트에 대한 내용을, object는 post하는 객체를, userInfo에는 key : value 형태의 dictionary를 리턴할 수 있습니다.

 

이벤트, userInfo에 대한 값을 enum으로 지정하겠습니다.

enum Notificaiion{
        enum Event{
            static let sendTalk = Foundation.Notification.Name.init(rawValue: "sendTalk")
        }
        enum Key{
            case talk
        }
    }

A가 톡을 보내는 이벤트를 Notification에 보낼 예정이기에 A클래스 내부에 enum을 선언하였습니다.

extension Notification을 하는 방법도 있지만, 이를 사용하게 되면 어느 객체에서 일어난 행위인지 구분하기 어려워지기에 저는 내부에 선언하여 사용하는 편을 선호합니다.

 

A의 전체코드

class A{
    func sendTalkToB(){
        NotificationCenter.default.post(name: Notificaiion.Event.sendTalk, object: self, userInfo: [Notificaiion.Key.talk : "hello world"])
    }
    
    enum Notificaiion{
        enum Event{
            static let sendTalk = Foundation.Notification.Name.init(rawValue: "sendTalk")
        }
        enum Key{
            case talk
        }
    }
}

그럼 

이를 수신하는 B에 대한 코드를 작성해보겠습니다.

NotificationCenter.default.addObserver(forName: <#T##NSNotification.Name?#>, object: <#T##Any?#>, queue: <#T##OperationQueue?#>, using: <#T##(Notification) -> Void#>)

수신구문입니다.

어떤 이벤트를, 어느 객체에서 오는 이벤트를, 어떤 큐에서, 어떤 작업을 할 지 등록합니다.

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

위와 같고 Selector를 사용하는 방법도 있습니다.

하지만 이는 objc의 가까운 코드에, 동기방식으로 이벤트를 송수신하기에, 저는 위의 방식을 조금 더 선호합니다. (하지만 두번째 방식을 사용했을 때도 불편함은 겪지 못하였습니다. )

 

class B{
	let a = A()
	init(){
        NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: a, queue: .main) { notification in
            guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
            print(talk)
        }
    }
}

B가 생성되면 해당 이벤트를 등록합니다.

이벤트 (A가 메시지를 보냈을 때)를 구독하고, B가 갖고 있는 a객체의 이벤트 수신만 송신하겠다. + 이를 받고 수행할 로직을 클로져에 작성했고, 이를 main큐에 예약하는 로직입니다.

 

생성이 끝나고 a.sendTalkToB()를 추가해보겠습니다.

전체코드입니다.

class B{
    let a = A()
    init(){
        NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: a, queue: .main) { notification in
            guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
            print(talk)
        }
        a.sendTalkToB()
    }
}

이제 B()를 실행해보면 hello world가 출력됩니다.

 

이전에 NotificationCenter를 공부하며 겪었던 문제는 object인자값을 활용할 때 있었습니다.

당시 A를 struct로 선언했었는 데, mutating으로 A의 전역변수 하나를 수정하였고, 그러자 B에서 A의 이벤트를 더 이상 감지하지 못하였습니다.

sturct는 "값"이라 내부의 값이 하나라도 변경되면 새로운 값이 되어 메모리에 올라가게 되는데, 그렇기에 B가 알던 이전의 A가 아니게 되어버린 것이였습니다..

 

모든곳에서 일어나는 이벤트를 받고 싶다면 addObserver의 object인자값을 nil로 설정하면 됩니다!

 

두번째 예제 코드입니다.

아까와 같지만, B,C는 init을 통하여 외부에서 같은 A를 받게 됩니다.

반면 D는 a의 값을 본인이 생성하여 D의 a 는 B, C의 a와는 다를 것입니다.

 

let a = A()
B(a: a)
C(a: a)
D()

a.sendTalkToB()
class B{
    init(a: A){
        NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: a, queue: .main) { notification in
            guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
            print("b heard \(talk)")
        }
    }
}

class C{
    init(a: A){
        NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: a, queue: .main) { notification in
            guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
            print("c heard \(talk)")
        }
    }
}


class D{
    let a = A()
    init(){
        NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: a, queue: .main) { notification in
            guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
            print("d heard \(talk)")
        }
    }
}

 

결과는 B, C의 print만 출력됩니다.

그럼 만약 D가 "내가 가진 a가 아니더라도, 모든 A가 말하는 이벤트를 구독하고 싶어"라고 하면

NotificationCenter.default.addObserver(forName: A.Notificaiion.Event.sendTalk, object: nil, queue: .main) { notification in
     guard let talk = notification.userInfo?[A.Notificaiion.Key.talk] as? String else { return }
     print("d heard \(talk)")
}

object의 인자값을 nil로 바꿔주면 성공!

 

NotificationCenter의 장점은 이와 같이 편리하게 이벤트를 구독, 수신할 수 있다!

단점은 이를 사용하는 모든 클래스에서 NotificationCenter에 대한 의존성이 생긴다. 라고 생각합니다.

다음에 이를 사용해서 Rx를 직접 구현해봐야겠네요!!

 

감사합니다!

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함