Skip to content

Commit 98f63d1

Browse files
Don't retain receiver of Completable.andThen beyond its completion (#2604)
1 parent 11c2d30 commit 98f63d1

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

RxSwift/Traits/PrimitiveSequence/Completable+AndThen.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ final private class ConcatCompletable<Element>: Producer<Element> {
7070
}
7171

7272
override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
73-
let sink = ConcatCompletableSink(parent: self, observer: observer, cancel: cancel)
74-
let subscription = sink.run()
73+
let sink = ConcatCompletableSink(second: second, observer: observer, cancel: cancel)
74+
let subscription = sink.run(completable: completable)
7575
return (sink: sink, subscription: subscription)
7676
}
7777
}
@@ -82,11 +82,11 @@ final private class ConcatCompletableSink<Observer: ObserverType>
8282
typealias Element = Never
8383
typealias Parent = ConcatCompletable<Observer.Element>
8484

85-
private let parent: Parent
85+
private let second: Observable<Observer.Element>
8686
private let subscription = SerialDisposable()
8787

88-
init(parent: Parent, observer: Observer, cancel: Cancelable) {
89-
self.parent = parent
88+
init(second: Observable<Observer.Element>, observer: Observer, cancel: Cancelable) {
89+
self.second = second
9090
super.init(observer: observer, cancel: cancel)
9191
}
9292

@@ -99,14 +99,14 @@ final private class ConcatCompletableSink<Observer: ObserverType>
9999
break
100100
case .completed:
101101
let otherSink = ConcatCompletableSinkOther(parent: self)
102-
self.subscription.disposable = self.parent.second.subscribe(otherSink)
102+
self.subscription.disposable = self.second.subscribe(otherSink)
103103
}
104104
}
105105

106-
func run() -> Disposable {
106+
func run(completable: Observable<Never>) -> Disposable {
107107
let subscription = SingleAssignmentDisposable()
108108
self.subscription.disposable = subscription
109-
subscription.setDisposable(self.parent.completable.subscribe(self))
109+
subscription.setDisposable(completable.subscribe(self))
110110
return self.subscription
111111
}
112112
}

Sources/AllTestz/main.swift

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ final class CompletableAndThenTest_ : CompletableAndThenTest, RxTestCase {
105105
("testCompletableCompleted_CompletableCompleted", CompletableAndThenTest.testCompletableCompleted_CompletableCompleted),
106106
("testCompletableError_CompletableCompleted", CompletableAndThenTest.testCompletableError_CompletableCompleted),
107107
("testCompletableCompleted_CompletableError", CompletableAndThenTest.testCompletableCompleted_CompletableError),
108+
("testCompletable_FirstCompletableNotRetainedBeyondCompletion", CompletableAndThenTest.testCompletable_FirstCompletableNotRetainedBeyondCompletion),
109+
("testCompletable_FirstCompletableNotRetainedBeyondFailure", CompletableAndThenTest.testCompletable_FirstCompletableNotRetainedBeyondFailure),
108110
("testCompletableEmpty_SingleCompleted", CompletableAndThenTest.testCompletableEmpty_SingleCompleted),
109111
("testCompletableCompleted_SingleNormal", CompletableAndThenTest.testCompletableCompleted_SingleNormal),
110112
("testCompletableError_SingleNormal", CompletableAndThenTest.testCompletableError_SingleNormal),

Tests/RxSwiftTests/Completable+AndThen.swift

+67
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,70 @@ extension CompletableAndThenTest {
120120
])
121121
}
122122

123+
func testCompletable_FirstCompletableNotRetainedBeyondCompletion() {
124+
let scheduler = TestScheduler(initialClock: 0)
125+
126+
let x: TestableObservable<Never> = scheduler.createHotObservable([
127+
.completed(210),
128+
])
129+
130+
var object = Optional.some(TestObject())
131+
132+
var completable = x.asCompletable()
133+
.do(onCompleted: { [object] in
134+
_ = object
135+
})
136+
137+
let disposable = completable
138+
.andThen(.never())
139+
.subscribe()
140+
141+
defer {
142+
disposable.dispose()
143+
}
144+
145+
// completable has completed by now
146+
scheduler.advanceTo(300)
147+
148+
weak var weakObject = object
149+
object = nil
150+
completable = .never()
151+
152+
XCTAssertNil(weakObject)
153+
}
154+
155+
func testCompletable_FirstCompletableNotRetainedBeyondFailure() {
156+
let scheduler = TestScheduler(initialClock: 0)
157+
158+
let x: TestableObservable<Never> = scheduler.createHotObservable([
159+
.error(210, TestError.dummyError),
160+
])
161+
162+
var object = Optional.some(TestObject())
163+
164+
var completable = x.asCompletable()
165+
.do(onCompleted: { [object] in
166+
_ = object
167+
})
168+
169+
let disposable = completable
170+
.andThen(.never())
171+
.subscribe()
172+
173+
defer {
174+
disposable.dispose()
175+
}
176+
177+
// completable has terminated with error by now
178+
scheduler.advanceTo(300)
179+
180+
weak var weakObject = object
181+
object = nil
182+
completable = .never()
183+
184+
XCTAssertNil(weakObject)
185+
}
186+
123187
#if TRACE_RESOURCES
124188
func testAndThenCompletableReleasesResourcesOnComplete() {
125189
_ = Completable.empty().andThen(Completable.empty()).subscribe()
@@ -575,3 +639,6 @@ extension CompletableAndThenTest {
575639
}
576640
#endif
577641
}
642+
643+
private class TestObject {
644+
}

0 commit comments

Comments
 (0)