Jak synchronizować dane między wątkami (kontekstem) w iOS?
sobes.tech AI
Odpowiedź od AI
Do synchronizacji można użyć następujących mechanizmów:
-
NSLock: Prosty blokujący mutex.
let lock = NSLock() func doSomething() { lock.lock() // Sekcja krytyczna kodu lock.unlock() } -
NSRecursiveLock: Pozwala jednemu wątkowi wielokrotnie przejąć blokadę bez deadlocka.
let recursiveLock = NSRecursiveLock() func recursiveFunction(level: Int) { recursiveLock.lock() if level < 3 { recursiveFunction(level: level + 1) } recursiveLock.unlock() } -
NSCondition: Pozwala wątkom oczekiwać na określony warunek przed kontynuacją.
let condition = NSCondition() var dataAvailable = false func producer() { condition.lock() // Produkcja danych dataAvailable = true condition.signal() // Sygnalizuj oczekującym wątkom condition.unlock() } func consumer() { condition.lock() while !dataAvailable { condition.wait() // Czekaj, aż warunek zostanie spełniony } // Przetwarzanie danych dataAvailable = false condition.unlock() } -
NSConditionLock: Mutex, który można przejąć tylko przy spełnieniu określonego warunku-wartości.
let conditionLock = NSConditionLock(condition: 0) let DATA_READY = 1 func producer() { conditionLock.lock(when: 0) // Przejąć, gdy warunek to 0 // Produkcja danych conditionLock.unlock(withCondition: DATA_READY) // Zwolnij i ustaw warunek na DATA_READY } func consumer() { conditionLock.lock(when: DATA_READY) // Przejąć, gdy warunek to DATA_READY // Przetwarzanie danych conditionLock.unlock(withCondition: 0) // Zwolnij i ustaw warunek na 0 } -
Dispatch Queues (GCD): Użycie kolejki dispatch sequential do dostępu do wspólnych zasobów.
let serialQueue = DispatchQueue(label: "com.yourapp.serialQueue") var sharedResource = 0 func updateResource() { serialQueue.async { // Dostęp do sharedResource tylko z tej kolejki sharedResource += 1 } } -
NSOperationQueue: Pozwala kontrolować zależności i priorytety operacji.
let operationQueue = OperationQueue() var sharedOperationResource = 0 let operation1 = BlockOperation { // Dostęp do sharedOperationResource sharedOperationResource += 1 } operationQueue.addOperation(operation1) -
Actors (Swift 5.5+): Zapewniają izolację stanu, zapobiegając jednoczesnemu dostępowi z różnych kontekstów.
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) } -
Konteksty Core Data: Do synchronizacji kontekstów danych można używać hierarchicznych kontekstów (rodzic-dziecko) lub mergeChanges(fromContextDidSaveNotification:).
// Przykład z hierarchią (rodzic - w tle, dziecko - UI) let privateMoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateMoc.persistentStoreCoordinator = persistentStoreCoordinator let mainMoc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) mainMoc.parent = privateMoc // Przykład z 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 { // Jeśli zapis nie nastąpił w głównym kontekście mainMoc.perform { mainMoc.mergeChanges(fromContextDidSave: notification) } } } -
Operacje atomowe: Użycie prymitywów atomowych (np. z
os/lock.h) dla prostych operacji.
Wybór metody zależy od złożoności zadania, typu danych i wymagań wydajnościowych. Aktorzy i GCD są nowoczesnymi, preferowanymi podejściami.