Created
April 29, 2020 14:48
-
-
Save pwc3/b3fad646f33e7ea7827244b0764fc8b1 to your computer and use it in GitHub Desktop.
Value Type Capture
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
class SimulatedOperation { | |
// Simulated function that collects data from multiple async calls. | |
// Build an array of [String] values, one element per async call. Hit the | |
// completion block once all of the elements are populated. | |
func collectData(completion: @escaping ([String]) -> Void) { | |
// Locally accumulate the results here. Why does this work? | |
// | |
// `Array` is a value type (struct). I expected the value to be | |
// captured when the closure was created, meaning: | |
// | |
// 1.) Modifying the array from a closure (passed to `getString` below) | |
// would not propagate back to the original variable. | |
// 2.) The closure passed to DispatchGroup.notify() would capture the | |
// array before all of the simulated work was done. | |
// | |
// Neither of those assumptions are correct! | |
// | |
// In Swift, captured variables are evaluated at the time of closure | |
// execution. In effect, it captures a reference (or pointer) to the | |
// variable. | |
// | |
// As a result, you can modify captured values in closures. | |
var result: [String] = [] | |
// Kick off some number of async operations. Use a DispatchGroup to get | |
// notified when all of the operations are done. | |
let group = DispatchGroup() | |
for i in 0..<100 { | |
group.enter() | |
getString(for: i) { | |
// We get called back on a serial queue, so we don't have to | |
// worry about concurrent access to `result`. | |
result.append($0) | |
group.leave() | |
} | |
} | |
// When all of the tasks in `group` are complete, use | |
// DispatchGroup.notify() to call our completion argument. | |
group.notify(queue: .main) { | |
completion(result) | |
} | |
} | |
// Serial background queue for simulated work. | |
let background = DispatchQueue(label: "queue", qos: .background) | |
// Do some simulated long-lasting work via DispatchQueue.asyncAfter. | |
func getString(for int: Int, completion: @escaping (String) -> Void) { | |
background.asyncAfter(deadline: .now() + 1) { | |
completion("\(int)") | |
} | |
} | |
} | |
// Make sure we get back and print the correct number of elements in the array. | |
let op = SimulatedOperation() | |
op.collectData { | |
print("Result: \($0)") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment