Cancellation handling in Swift - Time & Space Complexity
When we handle cancellation in Swift, we want to know how stopping a task early affects how long the program runs.
We ask: How does the program's work change when we cancel tasks?
Analyze the time complexity of the following code snippet.
func performTask(with items: [Int], isCancelled: () -> Bool) {
for item in items {
if isCancelled() {
break
}
// Simulate work
print("Processing \(item)")
}
}
This code processes a list of items but stops early if cancellation is detected.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through the array items one by one.
- How many times: Up to the number of items, but may stop earlier if cancelled.
Execution grows with the number of items unless cancellation happens early.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to 10, or fewer if cancelled early |
| 100 | Up to 100, or fewer if cancelled early |
| 1000 | Up to 1000, or fewer if cancelled early |
Pattern observation: The work grows linearly with input size but can be cut short by cancellation.
Time Complexity: O(n)
This means the work grows in a straight line with the number of items, but may be less if we stop early.
[X] Wrong: "Cancellation makes the code always run instantly or in constant time."
[OK] Correct: Cancellation only stops the work early sometimes; if not cancelled, the code still runs through all items.
Understanding how cancellation affects time helps you explain real-world task management and responsiveness in apps.
"What if we changed the cancellation check to happen only every 10 items? How would the time complexity change?"
Practice
Solution
Step 1: Understand cancellation handling concept
Cancellation handling is used to stop tasks that are no longer needed to save resources.Step 2: Compare options with concept
Only To stop tasks that are no longer needed matches the purpose of stopping unnecessary tasks.Final Answer:
To stop tasks that are no longer needed -> Option DQuick Check:
Cancellation handling = stop unnecessary tasks [OK]
- Thinking cancellation speeds up tasks
- Confusing cancellation with restarting tasks
- Assuming cancellation creates new tasks
Solution
Step 1: Recall correct API for cancellation check
The correct property to check cancellation is Task.isCancelled.Step 2: Verify syntax correctness
if Task.isCancelled { return } uses Task.isCancelled correctly; others use invalid method or property names.Final Answer:
if Task.isCancelled { return } -> Option BQuick Check:
Use Task.isCancelled to check cancellation [OK]
- Using incorrect method names like cancelled()
- Confusing property names with similar words
- Missing capitalization or spelling errors
Task {
for i in 1...3 {
try Task.checkCancellation()
print("Running step \(i)")
if i == 2 {
Task.current.cancel()
}
}
}Solution
Step 1: Understand Task.checkCancellation() behavior
Task.checkCancellation() throws if the task is cancelled, stopping further execution.Step 2: Analyze loop and cancellation timing
Cancellation happens when i == 2, so steps 1 and 2 print, but step 3 is skipped due to cancellation.Final Answer:
Running step 1\nRunning step 2 -> Option AQuick Check:
Cancellation after step 2 stops step 3 [OK]
- Assuming all steps run despite cancellation
- Ignoring that checkCancellation throws on cancel
- Thinking cancellation happens before step 2
Task {
if Task.isCancelled {
print("Cancelled")
}
try Task.checkCancellation()
print("Running")
}Solution
Step 1: Understand difference between Task.isCancelled and checkCancellation()
Task.checkCancellation() throws if cancelled, so it already handles cancellation detection.Step 2: Analyze redundancy of Task.isCancelled check
Checking Task.isCancelled before checkCancellation() is redundant because checkCancellation() will throw if cancelled.Final Answer:
Task.isCancelled check is redundant before checkCancellation() -> Option AQuick Check:
checkCancellation() alone suffices to detect cancellation [OK]
- Thinking isCancelled must come before checkCancellation()
- Missing try keyword (actually present here)
- Assuming no error when redundancy exists
func performTask() async {
let task = Task {
do {
for i in 1...5 {
try Task.checkCancellation()
print("Processing \(i)")
try await Task.sleep(nanoseconds: 1_000_000_000)
}
} catch {
print("Task was cancelled")
}
print("Cleanup done")
}
// User taps cancel button
task.cancel()
}Solution
Step 1: Analyze cancellation handling in the task
Task.checkCancellation() throws when cancelled, caught by catch block which prints cancellation message.Step 2: Verify cleanup code execution
Cleanup code runs after catch block, ensuring resources are cleaned up even after cancellation.Final Answer:
This code correctly cancels the task and runs cleanup after cancellation -> Option CQuick Check:
Cancellation triggers catch and cleanup runs [OK]
- Assuming cleanup won't run after cancellation
- Calling cancel() inside the task incorrectly
- Believing checkCancellation() does not throw
Ready to prove it?
Pass the test and this pattern moves unknown
