Sobes.tech
Middle

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:

  1. NSLock: Un mutex simple de verrouillage.

    let lock = NSLock()
    
    func doSomething() {
        lock.lock()
        // Section critique du code
        lock.unlock()
    }
    
  2. 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()
    }
    
  3. 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()
    }
    
  4. 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
    }
    
  5. 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
        }
    }
    
  6. 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)
    
  7. 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)
    }
    
  8. 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)
            }
        }
    }
    
  9. 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.