Comment synchroniser les données entre les threads (contexte) dans iOS?
sobes.tech IA
Réponse de l'IA
Pour la synchronisation, vous pouvez utiliser les mécanismes suivants:
-
NSLock: Un mutex simple de verrouillage.
let lock = NSLock() func doSomething() { lock.lock() // Section critique du code lock.unlock() } -
NSRecursiveLock: Permet à un thread de acquérir le verrou plusieurs fois sans deadlock.
let recursiveLock = NSRecursiveLock() func recursiveFunction(level: Int) { recursiveLock.lock() if level < 3 { recursiveFunction(level: level + 1) } recursiveLock.unlock() } -
NSCondition: Permet aux threads d'attendre une condition spécifique avant de continuer.
let condition = NSCondition() var dataAvailable = false func producer() { condition.lock() // Production de données dataAvailable = true condition.signal() // Signaler aux threads en attente condition.unlock() } func consumer() { condition.lock() while !dataAvailable { condition.wait() // Attendre que la condition soit remplie } // Traitement des données dataAvailable = false condition.unlock() } -
NSConditionLock: Un mutex qui ne peut être acquis que lorsque une condition-valeur spécifique est remplie.
let conditionLock = NSConditionLock(condition: 0) let DATA_READY = 1 func producer() { conditionLock.lock(when: 0) // Acquérir lorsque la condition est 0 // Production de données conditionLock.unlock(withCondition: DATA_READY) // Libérer et définir la condition à DATA_READY } func consumer() { conditionLock.lock(when: DATA_READY) // Acquérir lorsque la condition est DATA_READY // Traitement des données conditionLock.unlock(withCondition: 0) // Libérer et définir la condition à 0 } -
Dispatch Queues (GCD): Utilisation de files d'attente dispatch séquentielles pour accéder aux ressources partagées.
let serialQueue = DispatchQueue(label: "com.yourapp.serialQueue") var sharedResource = 0 func updateResource() { serialQueue.async { // Accès à sharedResource uniquement depuis cette file sharedResource += 1 } } -
NSOperationQueue: Permet de contrôler les dépendances et priorités des opérations.
let operationQueue = OperationQueue() var sharedOperationResource = 0 let operation1 = BlockOperation { // Accès à sharedOperationResource sharedOperationResource += 1 } operationQueue.addOperation(operation1) -
Actors (Swift 5.5+): Garantissent l'isolation de l'état, empêchant l'accès simultané depuis différents contextes.
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) } -
Contextes de Core Data: Pour synchroniser les contextes de données, vous pouvez utiliser des contextes hiérarchiques (parent-enfant) ou mergeChanges(fromContextDidSaveNotification:).
// Exemple avec hiérarchie (parent - en arrière-plan, enfant - UI) let privateMoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateMoc.persistentStoreCoordinator = persistentStoreCoordinator let mainMoc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) mainMoc.parent = privateMoc // Exemple avec 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 { // Si le sauvegarde n'a pas eu lieu dans le contexte principal mainMoc.perform { mainMoc.mergeChanges(fromContextDidSave: notification) } } } -
Opérations atomiques: Utilisation de primitives atomiques (par exemple, de
os/lock.h) pour des opérations simples.
Le choix de la méthode dépend de la complexité de la tâche, du type de données et des exigences de performance. Les acteurs et GCD sont des approches modernes préférées.