Quick answer: StopAllCoroutines leaves resources allocated (a download stream, a UI panel shown) because the coroutine’s “finally” cleanup never ran.
A loading coroutine opens a Stream then StopAllCoroutines is called on scene change; the stream isn’t closed. C# IEnumerator doesn’t honor try/finally on early stop.
Coroutines Are Not Tasks
StopCoroutine simply discards the IEnumerator. Any pending cleanup (try/finally, IDisposable) never runs. Treat coroutines as fire-and-forget for cleanup purposes.
Explicit Cleanup on Cancel
private bool isLoading;
IEnumerator Load() {
isLoading = true;
try { // work }
finally { CleanUp(); isLoading = false; }
}
void OnDisable() { if (isLoading) CleanUp(); }Belt-and-braces: even if the coroutine is yanked, OnDisable cleans up.
UniTask / Awaitable for Tokens
Use Unity 2023’s Awaitable or UniTask. They support CancellationToken — cooperative cancel that respects finally blocks.
Named Stop, Not Stop-All
Track Coroutine references and stop specifically with StopCoroutine(ref) — reduces collateral damage of stop-all.
Verifying
Cancelling a load mid-flight returns the script to a clean state. No leaked allocations or zombie UI panels.
“StopCoroutine kills the iterator. Cleanup must be explicit in OnDisable.”
Move new async logic to Awaitable + CancellationToken — cleaner cancel semantics and integrates with .NET async patterns.