Sobes.tech
Middle

Hoe synchroniseer je gegevens tussen threads (context) in iOS?

sobes.tech AI

Antwoord van AI

Voor synchronisatie kunnen de volgende mechanismen worden gebruikt:

  1. NSLock: Eenvoudige blokkerende mutex.

    let lock = NSLock()
    
    func doSomething() {
        lock.lock()
        // Kritische sectie code
        lock.unlock()
    }
    
  2. NSRecursiveLock: Maakt het mogelijk voor één thread om meerdere keren een lock te verkrijgen zonder deadlock.

    let recursiveLock = NSRecursiveLock()
    
    func recursiveFunction(level: Int) {
        recursiveLock.lock()
        if level < 3 {
            recursiveFunction(level: level + 1)
        }
        recursiveLock.unlock()
    }
    
  3. NSCondition: Laat threads wachten op een bepaalde voorwaarde voordat ze verder gaan.

    let condition = NSCondition()
    var dataAvailable = false
    
    func producer() {
        condition.lock()
        // Data productie
        dataAvailable = true
        condition.signal() // Signaleer wachtende threads
        condition.unlock()
    }
    
    func consumer() {
        condition.lock()
        while !dataAvailable {
            condition.wait() // Wacht tot de voorwaarde voldaan is
        }
        // Data verwerking
        dataAvailable = false
        condition.unlock()
    }
    
  4. NSConditionLock: Een mutex die alleen kan worden vastgehouden als aan een bepaalde voorwaarde-waarde wordt voldaan.

    let conditionLock = NSConditionLock(condition: 0)
    let DATA_READY = 1
    
    func producer() {
        conditionLock.lock(when: 0) // Vergrendel bij voorwaarde 0
        // Data productie
        conditionLock.unlock(withCondition: DATA_READY) // Ontgrendel en stel voorwaarde in op DATA_READY
    }
    
    func consumer() {
        conditionLock.lock(when: DATA_READY) // Vergrendel bij voorwaarde DATA_READY
        // Data verwerking
        conditionLock.unlock(withCondition: 0) // Ontgrendel en stel voorwaarde in op 0
    }
    
  5. Dispatch Queues (GCD): Gebruik van sequentiële dispatch-queues voor toegang tot gedeelde bronnen.

    let serialQueue = DispatchQueue(label: "com.yourapp.serialQueue")
    var sharedResource = 0
    
    func updateResource() {
        serialQueue.async {
            // Toegang tot sharedResource alleen via deze queue
            sharedResource += 1
        }
    }
    
  6. NSOperationQueue: Maakt het mogelijk om afhankelijkheden en prioriteiten van operaties te beheren.

    let operationQueue = OperationQueue()
    var sharedOperationResource = 0
    
    let operation1 = BlockOperation {
        // Toegang tot sharedOperationResource
        sharedOperationResource += 1
    }
    
    operationQueue.addOperation(operation1)
    
  7. Actors (Swift 5.5+): Bieden isolatie van toestand, voorkomen gelijktijdige toegang vanuit verschillende contexten.

    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. Core Data Contexts: Voor het synchroniseren van data-omgevingen kunnen hiërarchische contexts (ouder-kind) of mergeChanges(fromContextDidSaveNotification:) worden gebruikt.

    // Voorbeeld met hiërarchie (ouder - achtergrond, kind - UI)
    let privateMoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    privateMoc.persistentStoreCoordinator = persistentStoreCoordinator
    
    let mainMoc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    mainMoc.parent = privateMoc
    
    // Voorbeeld met 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 { // Als de save niet in de hoofdcontext plaatsvond
            mainMoc.perform {
                mainMoc.mergeChanges(fromContextDidSave: notification)
            }
        }
    }
    
  9. Atomic operations: Gebruik van atomare primitieve operaties (bijvoorbeeld uit os/lock.h) voor eenvoudige operaties.

De keuze van methode hangt af van de complexiteit van de taak, het type gegevens en de vereiste prestaties. Actors en GCD zijn moderne benaderingen die de voorkeur genieten.