Skip to content

Commit cb9c1f8

Browse files
authored
Revert "Fix Store leak when async effect is in flight (#2643)" (#2648)
* wip * wip
1 parent b86012c commit cb9c1f8

File tree

2 files changed

+44
-45
lines changed

2 files changed

+44
-45
lines changed

Sources/ComposableArchitecture/Store.swift

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,6 @@ public final class Store<State, Action> {
485485
for id in self.children.keys {
486486
self.invalidateChild(id: id)
487487
}
488-
self.effectCancellables.values.forEach { cancellable in
489-
cancellable.cancel()
490-
}
491-
self.effectCancellables.removeAll()
492488
}
493489

494490
fileprivate func invalidateChild(id: ScopeID<State, Action>) {
@@ -530,14 +526,14 @@ public final class Store<State, Action> {
530526
defer { index += 1 }
531527
let action = self.bufferedActions[index]
532528
let effect = self.reducer.reduce(into: &currentState, action: action)
533-
let uuid = UUID()
534529

535530
switch effect.operation {
536531
case .none:
537532
break
538533
case let .publisher(publisher):
539534
var didComplete = false
540535
let boxedTask = Box<Task<Void, Never>?>(wrappedValue: nil)
536+
let uuid = UUID()
541537
let effectCancellable = withEscapedDependencies { continuation in
542538
publisher
543539
.handleEvents(
@@ -575,48 +571,45 @@ public final class Store<State, Action> {
575571
}
576572
case let .run(priority, operation):
577573
withEscapedDependencies { continuation in
578-
let task = Task(priority: priority) { @MainActor [weak self] in
579-
#if DEBUG
580-
let isCompleted = LockIsolated(false)
581-
defer { isCompleted.setValue(true) }
582-
#endif
583-
await operation(
584-
Send { effectAction in
585-
#if DEBUG
586-
if isCompleted.value {
587-
runtimeWarn(
588-
"""
589-
An action was sent from a completed effect:
590-
591-
Action:
592-
\(debugCaseOutput(effectAction))
593-
594-
Effect returned from:
595-
\(debugCaseOutput(action))
596-
597-
Avoid sending actions using the 'send' argument from 'Effect.run' after \
598-
the effect has completed. This can happen if you escape the 'send' \
599-
argument in an unstructured context.
600-
601-
To fix this, make sure that your 'run' closure does not return until \
602-
you're done calling 'send'.
603-
"""
604-
)
574+
tasks.wrappedValue.append(
575+
Task(priority: priority) { @MainActor in
576+
#if DEBUG
577+
let isCompleted = LockIsolated(false)
578+
defer { isCompleted.setValue(true) }
579+
#endif
580+
await operation(
581+
Send { effectAction in
582+
#if DEBUG
583+
if isCompleted.value {
584+
runtimeWarn(
585+
"""
586+
An action was sent from a completed effect:
587+
588+
Action:
589+
\(debugCaseOutput(effectAction))
590+
591+
Effect returned from:
592+
\(debugCaseOutput(action))
593+
594+
Avoid sending actions using the 'send' argument from 'Effect.run' after \
595+
the effect has completed. This can happen if you escape the 'send' \
596+
argument in an unstructured context.
597+
598+
To fix this, make sure that your 'run' closure does not return until \
599+
you're done calling 'send'.
600+
"""
601+
)
602+
}
603+
#endif
604+
if let task = continuation.yield({
605+
self.send(effectAction, originatingFrom: action)
606+
}) {
607+
tasks.wrappedValue.append(task)
605608
}
606-
#endif
607-
if let task = continuation.yield({
608-
self?.send(effectAction, originatingFrom: action)
609-
}) {
610-
tasks.wrappedValue.append(task)
611609
}
612-
}
613-
)
614-
self?.effectCancellables[uuid] = nil
615-
}
616-
tasks.wrappedValue.append(task)
617-
self.effectCancellables[uuid] = AnyCancellable {
618-
task.cancel()
619-
}
610+
)
611+
}
612+
)
620613
}
621614
}
622615
}

Tests/ComposableArchitectureTests/StoreLifetimeTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ final class StoreLifetimeTests: BaseTCATestCase {
6464
}
6565

6666
func testStoreDeinit_RunningEffect() async {
67+
XCTTODO(
68+
"We would like for this to pass, but it requires full deprecation of uncached child stores"
69+
)
6770
Logger.shared.isEnabled = true
6871
let effectFinished = self.expectation(description: "Effect finished")
6972
do {
@@ -90,6 +93,9 @@ final class StoreLifetimeTests: BaseTCATestCase {
9093
}
9194

9295
func testStoreDeinit_RunningCombineEffect() async {
96+
XCTTODO(
97+
"We would like for this to pass, but it requires full deprecation of uncached child stores"
98+
)
9399
Logger.shared.isEnabled = true
94100
let effectFinished = self.expectation(description: "Effect finished")
95101
do {

0 commit comments

Comments
 (0)