Educational iOS SwiftUI app demonstrating memory management patterns in async Swift code
An interactive playground showcasing different approaches to handling weak references, task cancellation, and memory leaks in asynchronous Swift operations. Perfect for learning Swift concurrency best practices and understanding common pitfalls.
This app demonstrates the critical differences between various memory management patterns when dealing with:
- Single async tasks vs infinite async sequences
- Weak self capture vs task cancellation vs guard let patterns
- Memory leaks and proper cleanup strategies
- SingleTask Tab - Memory management for finite async operations
- AsyncSequence Tab - Memory management for infinite async streams
- Live console output showing object lifecycles
- Real-time memory leak detection through deinit messages
- Immediate feedback via app switching notifications
The app uses a View - Interactor - Presenter (VIP) pattern to build screens just to show how Tasks retain entities and what stays in memory. (VIP just has a lot of components, very convenient for the demo purposes).
Interactor: Handles async operations and business logicPresenter: Manages state updates and UI communicationViewController: UIKit view controller with progress indicatorLongService: Simulates long-running async operationsUIViewControllerRepresentable: Bridges UIKit to SwiftUI
The app demonstrates 10 different approaches to async memory management:
| Pattern | Description | Memory Behavior | Use Case |
|---|---|---|---|
| NoWeakInteractor | No capture list | Everything in memory until Task finishes | ❌ Demonstrates the problem |
| WeakInteractor | [weak self] capture |
Immediate deallocation, task continues | ✅ Fire-and-forget operations |
| CancellationInteractor | Task storage + cancellation | Immediate cleanup + task termination | ✅ User-initiated operations |
| GuardLetInteractor | [weak self] + guard let self |
Extended lifetime until task completes | |
| DeinitCancellationInteractor | Cancels task in deinit |
Cleanup when object is deallocated | ✅ Alternative cleanup timing |
| Pattern | Description | Memory Behavior | Use Case |
|---|---|---|---|
| InfiniteSequenceInteractor | No capture list | Memory leak until app termination | ❌ Demonstrates infinite leak |
| WeakSequenceInteractor | [weak self] capture |
Immediate deallocation, sequence continues | |
| CancelSequenceInteractor | Task storage + cancellation | Immediate cleanup + sequence termination | ✅ Recommended |
| GuardLetSequenceInteractor | [weak self] + guard let self |
Memory leak until app termination | ❌ Catastrophic pattern |
| DeinitCancelSequenceInteractor | [weak self] + deinit cancellation |
Immediate deallocation + sequence cleanup | ✅ Flexible cleanup timing |
- Open project in Xcode 16.3+
- Run on iOS 18.4+ simulator or device
- Navigate between tabs and test each pattern
- Tap any pattern button to open the demo
- Immediately dismiss the sheet (swipe down)
- Watch console output for
deinitmessages - Missing deinit = Memory leak detected 🚨
- Open any AsyncSequence pattern
- Switch to another app and back (triggers notifications)
- Dismiss the sheet
- Switch apps again - watch for continued console logs
- Continued logs = sequence still running (potential issue)
- Async operations can outlive their creating views
- Infinite sequences are more dangerous than single tasks
- Context determines pattern appropriateness
- Cancellation is essential for sequences, optional(but still highly recommended!) for tasks
- Forgetting capture lists in async contexts
- Using
guard let selfin long-running operations without cancellation handling - Not canceling infinite async sequences
- Assuming weak self always prevents memory issues
- iOS 18.4+
- Swift 6.0 with strict concurrency
- Xcode 16.3+
- Uses modern Swift concurrency (
async/await,Task,AsyncSequence)
-
Clone the repository
git clone https://github.com/SergeyPetrachkov/CancellationVsWeakSelfPlayground cd ConcurrencyPlayground -
Open in Xcode
open ConcurrencyPlayground.xcodeproj
-
Run the app
- Select a simulator or device
- Press
Cmd+Rto build and run
-
Start exploring
- Try each pattern in both tabs
- Watch console output carefully
- Experiment with different timing scenarios
This playground is ideal for:
- iOS developers learning Swift concurrency
- Code review training on memory management
- Team workshops on async programming best practices
- Interview preparation on iOS memory management
This is an educational project. Feel free to:
- Add new memory management patterns
- Improve documentation
- Suggest additional test scenarios
- Report issues or unclear explanations
- Store and cancel tasks in
viewDidUnload()ordeinitwhen appropriate - Choose cancellation timing based on cleanup requirements:
viewDidUnload()for view-lifecycle bound operationsdeinitfor more flexible cleanup timing
- Avoid
guard let selffor operations that outlive the view controller