class DownloadCounter {
var count = 0
var lastDownload: Date?
}
actor DownloadManager {
private var totalProcessed = 0 // β
Protected by actor
func processDownload(_ counter: DownloadCounter) async {
totalProcessed += 1 // β
Safe - actor's state
// π₯ DANGER: counter is NOT protected!
counter.count += 1 // β Potential data race
counter.lastDownload = Date() // β Potential data race
}
}
// Data race scenario
func dangerousUsage() async {
let counter = DownloadCounter()
let manager = DownloadManager()
// Thread 1
Task {
await manager.processDownload(counter)
}
// Thread 2 - Concurrent access!
Task {
counter.count += 5 // π₯ DATA RACE with Thread 1
}
}β The Solution: sending to Transfer Ownership
actor DownloadManager {
private var totalProcessed = 0
func processDownload(_ counter: sending DownloadCounter) async {
totalProcessed += 1
// β
Safe now - counter belongs exclusively to me
counter.count += 1
counter.lastDownload = Date()
}
}
func safeUsage() async {
let counter = DownloadCounter()
let manager = DownloadManager()
await manager.processDownload(counter)
// counter is no longer accessible here β
}π‘ Tip: When passing a mutable object to an actor, ask yourself: “Who else can access this?” If the answer isn’t “nobody”, use sending or a safe alternative!