Come sincronizzare i dati tra thread (contesto) in iOS?
sobes.tech AI
Risposta dell'AI
Per la sincronizzazione, è possibile utilizzare i seguenti meccanismi:
-
NSLock: Un mutex di blocco semplice.
let lock = NSLock() func doSomething() { lock.lock() // Sezione critica del codice lock.unlock() } -
NSRecursiveLock: Permette a un thread di acquisire più volte il lock senza deadlock.
let recursiveLock = NSRecursiveLock() func recursiveFunction(level: Int) { recursiveLock.lock() if level < 3 { recursiveFunction(level: level + 1) } recursiveLock.unlock() } -
NSCondition: Permette ai thread di attendere una condizione specifica prima di continuare.
let condition = NSCondition() var dataAvailable = false func producer() { condition.lock() // Produzione dati dataAvailable = true condition.signal() // Segnala ai thread in attesa condition.unlock() } func consumer() { condition.lock() while !dataAvailable { condition.wait() // Attende che la condizione sia soddisfatta } // Elaborazione dati dataAvailable = false condition.unlock() } -
NSConditionLock: Un mutex che può essere acquisito solo quando una condizione-valore specifica è soddisfatta.
let conditionLock = NSConditionLock(condition: 0) let DATA_READY = 1 func producer() { conditionLock.lock(when: 0) // Acquisisci quando la condizione è 0 // Produzione dati conditionLock.unlock(withCondition: DATA_READY) // Rilascia e imposta la condizione a DATA_READY } func consumer() { conditionLock.lock(when: DATA_READY) // Acquisisci quando la condizione è DATA_READY // Elaborazione dati conditionLock.unlock(withCondition: 0) // Rilascia e imposta la condizione a 0 } -
Dispatch Queues (GCD): Uso di code di dispatch sequenziali per l'accesso alle risorse condivise.
let serialQueue = DispatchQueue(label: "com.yourapp.serialQueue") var sharedResource = 0 func updateResource() { serialQueue.async { // Accesso a sharedResource solo da questa coda sharedResource += 1 } } -
NSOperationQueue: Permette di controllare dipendenze e priorità delle operazioni.
let operationQueue = OperationQueue() var sharedOperationResource = 0 let operation1 = BlockOperation { // Accesso a sharedOperationResource sharedOperationResource += 1 } operationQueue.addOperation(operation1) -
Actors (Swift 5.5+): Garantiscono l'isolamento dello stato, prevenendo accessi simultanei da diversi contesti.
actor Counter { private var count = 0 func increment() { count += 1 } func getCount() -> Int { return count } } let counter = Counter() Task { await counter.increment() let currentCount = await counter.getCount() print(currentCount) } -
Contesti di Core Data: Per sincronizzare i contesti di dati, si possono usare contesti gerarchici (genitore-figlio) o mergeChanges(fromContextDidSaveNotification:).
// Esempio con gerarchia (genitore - in background, figlio - UI) let privateMoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateMoc.persistentStoreCoordinator = persistentStoreCoordinator let mainMoc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) mainMoc.parent = privateMoc // Esempio con mergeChanges NotificationCenter.default.addObserver(self, selector: #selector(contextDidSave(_:)), name: .NSManagedObjectContextDidSave, object: nil) @objc func contextDidSave(_ notification: Notification) { guard let savingContext = notification.object as? NSManagedObjectContext else { return } if savingContext != mainMoc { // Se il salvataggio non è avvenuto nel contesto principale mainMoc.perform { mainMoc.mergeChanges(fromContextDidSave: notification) } } } -
Operazioni atomiche: Uso di primitive atomiche (ad esempio, da
os/lock.h) per operazioni semplici.
La scelta del metodo dipende dalla complessità del compito, dal tipo di dati e dai requisiti di prestazioni. Gli attori e GCD sono approcci moderni preferiti.