티스토리 뷰
안녕하세요 :) Zedd입니다.
오늘은..
-
Cancellable
마지막..!! Cancellable을 공부해볼게요!
Combine을 공부하고 그걸 블로그에 포스팅 하는 입장으로서, 틀린 내용을 게시하지 않으려고 노력중입니다!
그럼에도 불구하고 틀린 내용이 있을 수 있으니
이 글만 보고 아 이게 이렇구나~라는 건 굉장히 위험합니다.
그걸 꼭 염두하시고 글을 읽으시길 바랄게요 :D
틀린 내용을 발견하시면 댓글로 알려주시면 감사하겠습니다!
Combine이 유독......뭔가 조심스럽네요. 제가 1도 몰라서리 ㅎ
그럼 Cancellable을 공부해봅시다!
Cancellable 역시 프로토콜이구요!
정의는 activity 또는 action이 취소(cancellation)를 지원함을 나타내는 프로토콜이라고 합니다..!
cancel()을 호출하면 할당 된 모든 리소스가 해제된대요.
타이머, 네트워크 액세스, 디스크 IO와 같은 부작용을 막는다고 해요.
이 Cancellable은 우리가..? 아니..제가..공부 한 적은 없지만 많이 봤어요!!
바로 sink의 result...

ㅇ ㅏ Cancellable인 줄 ㅎ AnyCancellable이네
그럼 AnyCancellable을 공부해봅시다!
AnyCancellable은 프로토콜은 아니고 class인데요.
딱 봐도 Cancellable 채택하게 생겼다 아님

응 맞아
AnyCancellable은 취소 되었을 때, 제공된 closure를 실행하는 type-erasing cancellable object입니다.
Subscriber 구현에서는 이 타입을 사용해서 caller가 publisher를 취소할 수 있지만, Subscription 객체를 사용하여 item을 요청할 수 없는 "cancellation token"을 제공할 수 있습니다.
AnyCancellable 인스턴스는 deinitialized 될 때 자동으로 cancel()을 호출합니다.
자..! 그럼 cancel()을 명시적으로 호출해봅시다.
별로 어렵진 않은데요,
let subject = PassthroughSubject<Int, Never>() | |
let subscriber = subject.sink(receiveValue: { value in | |
print(value) | |
}) | |
subscriber.cancel() | |
subject.send(1) |
이렇게 하면 되겠죠?!?!
send를 호출해도 아무 반응 없음 ㅎ 왜냐면 cancel 됐으니까..!!!
AnyCancellable의 생성자는 cancel()이 호출될 때 실행할 closure를 받는데요, sink에서...알아서 넣겠죠..!?

실제로, subscriber를 dump해보면..

이렇게 나옵니다.
cancel하고 나서는

이러케 nil...
그럼 제가 궁금한 점은..
sink로 subscriber를 만들었는데...cancel()을 호출했어.
근데 내가 원하는 클로져 실행시키고 싶어 < 이거 안되는건가요...!?!?!
저 클로져 안에 뭐가 들어있는거지..?
자..cancel되었을 때 내 closure를 실행시키는 방법은 이 글 후반부에서 다루도록 할게요.
먼저 Cancellable의 store(in: ) 메소드를 보도록 할게요!

자 store(in: ) 메소드는 뭐냐면..말그대로 store..저장하는 겁니다.
뭐를? Cancellable instance를...
그래서 어떻게 사용하면 되냐면..
var bag = Set<AnyCancellable>() | |
let subject = PassthroughSubject<Int, Never>() | |
subject | |
.sink(receiveValue: { value in | |
print(value) | |
}) | |
.store(in: &bag) |
store메소드안에 보면 파라미터의 타입이 Set<AnyCancellable>인걸 볼 수 있죠?
응 그니까 Set<AnyCancellable>을 만들어주고, 그걸 그냥 store에 넘기면 됩니다. 짱쉽죠...
아무튼 store(in: )메소드의 사용법은 알겠죠?
그럼 Subscription객체를 이용해서..취소되었을 때 내가 실행하고 싶은 closure를 실행하는 방법을 볼게요 ㅎㅎ
class ZeddSubscriber: Subscriber { | |
typealias Input = String | |
typealias Failure = Never | |
var subscription: Subscription? | |
func receive(subscription: Subscription) { | |
subscription.request(.unlimited) | |
self.subscription = subscription | |
} | |
func receive(_ input: String) -> Subscribers.Demand { | |
return .unlimited | |
} | |
func receive(completion: Subscribers.Completion<Never>) {} | |
} |
자..custom Subscriber를 만들어주었습니다. https://zeddios.tistory.com/966
Combine (1-1) - Subcribers.Demand
안녕하세요 :) Zedd입니다. https://zeddios.tistory.com/925 Combine (1) - Publisher, Subscriber 안녕하세요 :) Zedd입니다. 이번 휴가동안 SwiftUI를 좀 해볼라하는데 Combine모르면 이거 걍 노답임.... -> 휴..
zeddios.tistory.com
에서 조금 수정한 예제에요. 위 글을 보신 분이라면..익숙하실거에요.
위 코드에서 눈여겨 봐야 할 점은,

Subscription 타입 변수가 있다는 겁니다!
이 Subscription 역시 프로토콜이구요. Subscription은 Cancellable을 채택하고 있습니다.
그럼 custom Subscription 타입을 만들어볼게요.
final class ZeddSubscription: Subscription { | |
private let cancellable: Cancellable | |
init(_ cancel: @escaping () -> Void) { | |
self.cancellable = AnyCancellable(cancel) | |
} | |
func request(_ demand: Subscribers.Demand) {} | |
func cancel() { | |
self.cancellable.cancel() | |
} | |
} |
를 참고했어요! (클래스 이름만 바꿈)
그럼 ZeddSubscription 인스턴스를 만들어서 ZeddSubscriber의 subscription안에 넣어주면 되겠죠?
let subject = PassthroughSubject<String, Never>() | |
let subscriber = ZeddSubscriber() | |
subject.print("Combine Test").subscribe(subscriber) | |
subscriber.subscription = ZeddSubscription({ | |
print("cancel!") | |
}) | |
subject.send("Zedd") | |
subject.send("Alan") | |
subscriber.subscription?.cancel() |
이렇게 해줘볼게요!
subscribe앞에 print()를 호출했기 때문에 실행하면 콘솔에 로그들이 뜰거에요! 저번글에서는 단순히 print().~이런식으로 했는데 오늘은
print 안에 그냥 아무 String을 넣어봤어요~ 이렇게도 쓸 수 있다는 걸 알려드리고 싶었습니다..!

이렇게 뜨게 됩니다 ㅎㅎㅎ
자, 그럼 cancel되었을 때 "cancel!" 이 출력된 걸 볼 수 있죠!!!
내가 넣은 클로져가 실행이 되었습니다!!!
근데 지금 이거는..custom Subscriber만들고..
custom Subscription까지 만들어서......ㅎㅎㅎ 한거잖아요..!! 귀찮귀찮
Apple이 제공해주는 subscriber를 만들어주는 sink..를 가지고 cancel 되었는지 어쩐지를 판별을 어떻게 할 수 있을까요!?
찾아보니 handleEvent라는게 있어요!
정의는
"Performs the specified closures when publisher events occur."
publisher 이벤트가 발생 할 때 지정한 closure를 수행한다...입니다!
사용방법을 볼게요!
let subject = PassthroughSubject<String, Error>() | |
let subscriber = subject.handleEvents(receiveSubscription: { (subscription) in | |
print("Receive Subscription") // 2 | |
}, receiveOutput: { output in | |
print("Receive Output : \(output)") // 3 | |
}, receiveCompletion: { (completion) in | |
print("Receive Completion") | |
switch completion { | |
case .finished: | |
print("finished") | |
case .failure(let error): | |
print(error) | |
} | |
}, receiveCancel: { | |
print("Receive Cancel") | |
}, receiveRequest: { demand in | |
print("Receive Request: \(demand)") // 1 | |
}).sink(receiveCompletion: { (completion) in | |
switch completion { | |
case .finished: | |
print("finished") | |
case .failure(let error): | |
print(error) | |
} | |
}, receiveValue: { (value) in | |
print("Receive Value in sink : \(value)") // 4 | |
}) | |
subject.send("Zedd") | |
subscriber.cancel() |
이렇게 사용하시면 됩니다.
당연히 sink를 써서;; 구독을 해야 send고 뭐고 작동을 하겠죠?
아 근데 진짜 아쉬운게 sink안에 있으면 안되는거야..>!?!?!?!?!?!
코드가 쓸데없이 길어지는 느낌인데..ㅠㅠ
그래도 있는게 어디..라며...
감사해봅니다...
일단 제가 대충 정한..그러니까 Combine의 가장 대표적인 프로토콜들은 다 공부를 했네요..!
그래도 봐야 할게 엄청나게 많이 남았지만..ㅎㅎ 도움이 되었길 바랍니다.
참고
https://developer.apple.com/documentation/combine/cancellable
Cancellable - Combine | Apple Developer Documentation
A type-erasing cancellable object that executes a provided closure when canceled.
developer.apple.com
https://www.avanderlee.com/debugging/combine-swift/
Combine debugging using operators in Swift - SwiftLee
Combine debugging can be hard because of the long stack traces and asynchronous callbacks. Learn how to benefit from Combine operators to solve this issue.
www.avanderlee.com
'Combine' 카테고리의 다른 글
Combine ) ConnectablePublisher (0) | 2020.04.26 |
---|---|
Combine + UIKit (2) | 2020.04.19 |
Combine (3) - Scheduler (4) | 2020.03.23 |
Combine (2) - Subject (0) | 2020.02.29 |
Combine (1-1) - Subcribers.Demand (0) | 2020.02.26 |
- 피아노
- 스위프트 문법
- WKWebView
- WWDC
- swift 공부
- np-hard
- ios 13
- np-complete
- iOS delegate
- actor
- Combine
- swift delegate
- Swift
- 제이슨 파싱
- IOS
- WidgetKit
- swift sort
- FLUTTER
- 회고
- Git
- swift array
- swift3
- Xcode
- Accessibility
- UIBezierPath
- SwiftUI
- fastlane
- swift tutorial
- github
- 스위프트
- Total
- Today
- Yesterday