ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Combine - multicast / share
    Combine 2022. 3. 24. 20:01

    안녕하세요. 그린입니다🟢

    이번 포스팅에서는 다시 컴바인 세계로 돌아와 multicast와 share에 대해 알아보겠습니다🙏🏻

     

    우선 어떤걸 하는 친구들이냐!?
    하나의 Publisher로 부터 구독을 한 여러 Subscriber들에게 각각 이벤트를 여러번 발행하는게 아니라
    하나의 요소에 한번의 이벤트만 발생시켜줄 수 있도록 해주는 친구들입니다🙌

     

    이번건 꽤나 간단히 이해될 수 있으니 한번 해보시죠!

    multicast

    공식문서를 보면 클로저를 적용해 구독자에게 요소를 전달하는 subject를 만들어준다고해요.

    역시 공식문서의 정의는 어려워...🤯

    우선 그러려니~ 하고 정의된 코드를 보시죠!

    func multicast<S>(_ createSubject: @escaping () -> S) -> Publishers.Multicast<Self, S> where S : Subject, Self.Failure == S.Failure, Self.Output == S.Output

    요렇게 되어있어요. 

    아직까지 감이 안오시죠?

    그럼 기존에는 어떻게 사용되었는지 또 multicast가 어떻게 사용될지 보시죠!

     

    [multicast 미사용]

    let pub = ["First", "Second", "Third"].publisher
        .map( { return ($0, Int.random(in: 0...100)) } )
        .print("Random")
    
    cancellable1 = pub
       .sink { print ("Stream 1 received: \($0)")}
    cancellable2 = pub
       .sink { print ("Stream 2 received: \($0)")}
    
    // Random: receive value: (("First", 9))
    // Stream 1 received: ("First", 9)
    // Random: receive value: (("Second", 46))
    // Stream 1 received: ("Second", 46)
    // Random: receive value: (("Third", 26))
    // Stream 1 received: ("Third", 26)
    // Random: receive value: (("First", 9))
    // Stream 2 received: ("First", 9)
    // Random: receive value: (("Second", 46))
    // Stream 2 received: ("Second", 46)
    // Random: receive value: (("Third", 26))
    // Stream 2 received: ("Third", 26)

    기존에는 이렇게 각 pub에서의 발행이 2번 이루어졌어요.

    지금이야 크게 문제가 없지만 만약 통신이나 방대한 서비스에 대해 여러 구독자가 구독을 하고 있으면 낭비겠죠?

    같은걸 여러 구독자때문에 여러번 발행하는 형태이니까요!

    그래서 multicast를 사용하면 이롭습니다🙌

     

    multicast도 이제 쉽게 코드로 보면서 감을 찾아보시죠!

    (코드는 공식문서에서 예제 코드를 가져왔습니다)

     

    [multicast 사용]

    let pub = ["First", "Second", "Third"].publisher
        .map( { return ($0, Int.random(in: 0...100)) } )
        .print("Random")
        .multicast { PassthroughSubject<(String, Int), Never>() }
    
    cancellable1 = pub
       .sink { print ("Stream 1 received: \($0)")}
    cancellable2 = pub
       .sink { print ("Stream 2 received: \($0)")}
       
    pub.connect()
    
    // Random: receive value: (("First", 9))
    // Stream 2 received: ("First", 9)
    // Stream 1 received: ("First", 9)
    // Random: receive value: (("Second", 46))
    // Stream 2 received: ("Second", 46)
    // Stream 1 received: ("Second", 46)
    // Random: receive value: (("Third", 26))
    // Stream 2 received: ("Third", 26)
    // Stream 1 received: ("Third", 26)

    짜란 일단 결과를 보면 한번 발행으로 여러 구독자에서 받아줍니다!

    편안....

    달라진 부분을 보면 publisher에서 multicast 클로저 블럭을 넣어줘요.

    그리고 마지막에 connect()를 해줘야 정상적으로 사용이 됩니다!

    connect 관련해서는 왜 그런지 생각해보면 multicast를 해주면 Publishers.Multicast로 타입이 변환되요.

    이 Multicast 타입을 까보면 ConnectablePublisher를 채택하고 있습니다.

    저 친구의 특성상 connect()를 호출해야 요소들을 방출합니다.

    이 부분은 다음 포스팅에서 ConnectablePublisher이 뭔지 조금 더 자세히 알아보기로 하죠!🙋🏻

     

    그러면 이제 share가 남았네요?
    뭘까요 이녀석은 도대체🙃

    share

    공식문서의 설명부터 보죠!

    업스트림 게시자의 출력을 여러 구독자와 공유하도록 해주는 메서드라고 합니다.

    multicast와 유사해보이죠? 이렇게 묶어서 포스팅하는것도 이유가 될것 같네요🙌

    func share() -> Publishers.Share<Self>

    선언은 요렇습니다.

    이 메서드를 호출하여 반환된 Publisher는 여러 Subscriber들을 지원하고 모두 변경되지 않은 요소들과 완료 여부 상태를 받습니다.

    우리 위에서 multicast할때 ConnectablePublisher를 따르는 Multicast 타입이 됨으로 connect()를 꼭 호출해줘야 발행된다고 했죠?

    이 친구는 그런거 없습니다.

    왜냐면, 내부적으로 autoconnect()를 호출하고 있기 때문이죠!

    즉 connect()를 호출하지 않아도 Subscriber에서 구독해주는 순간부터 받고 있게 됩니다.

    대신 이것때문에 중요한게 있습니다.

    여러 구독자가 sink해주는 시점이 다를텐데 첫 구독자가 sink해줄때 바로 방출되게 된다면 안되겠죠?

    그래서 delay를 걸어줘야합니다.

     

    "Talk is cheap. Show me the code."

     

    네.. 이 문구 참 맘에 드네요🙃

    (동일하게 공식문서의 예제 코드를 가져왔습니다.)

    let pub = (1...3).publisher
        .delay(for: 1, scheduler: DispatchQueue.main)
        .map( { _ in return Int.random(in: 0...100) } )
        .print("Random")
        .share()
    
    cancellable1 = pub
        .sink { print ("Stream 1 received: \($0)")}
    cancellable2 = pub
        .sink { print ("Stream 2 received: \($0)")}
    
    // Random: receive value: (20)
    // Stream 1 received: 20
    // Stream 2 received: 20
    // Random: receive value: (85)
    // Stream 1 received: 85
    // Stream 2 received: 85
    // Random: receive value: (98)
    // Stream 1 received: 98
    // Stream 2 received: 98

    publisher를 생성할때 delay를 걸어주는 이유는 위에 설명했습니다.

    이어 share()만 호출하여 붙여줘 이 Publisher의 타입을 Publiser.Share로 만들어줍니다.

    그리고 여러 구독자에서 이 publisher를 구독하면 하나의 발행에서 위에서 했던 multicast의 방식과 동일하게 진행됩니다!

    둘이 참 비슷하네요🙌

    마무리

    이렇게 multicast와 share에 대해 알아보았습니다!

    둘다 비슷한 쓰임이라 더 쉬웠던 이해흐름이 되었어요ㅎㅎ

    아직 갈길멀다 combine......🏃🏻

    [참고자료]

    https://developer.apple.com/documentation/combine/publisher/multicast(_:) 

     

    Apple Developer Documentation

     

    developer.apple.com

    https://developer.apple.com/documentation/combine/publisher/share() 

     

    Apple Developer Documentation

     

    developer.apple.com

     

    'Combine' 카테고리의 다른 글

    Combine - ConnectablePublisher  (0) 2022.03.28
    Combine - ObservableObject / @Published / @ObservedObject  (0) 2022.03.17
    Combine - Cancellable  (2) 2022.03.10
    Combine - Scheduler  (0) 2022.03.07
    Combine - Subject  (0) 2022.02.28
Designed by Tistory.