¿Cómo sincronizar datos entre hilos (contexto) en iOS?
sobes.tech AI
Respuesta de la IA
Para la sincronización, se pueden utilizar los siguientes mecanismos:
-
NSLock: Un mutex simple de bloqueo.
let lock = NSLock() func doSomething() { lock.lock() // Sección crítica del código lock.unlock() } -
NSRecursiveLock: Permite que un hilo adquiera el bloqueo varias veces sin deadlock.
let recursiveLock = NSRecursiveLock() func recursiveFunction(level: Int) { recursiveLock.lock() if level < 3 { recursiveFunction(level: level + 1) } recursiveLock.unlock() } -
NSCondition: Permite que los hilos esperen una condición específica antes de continuar.
let condition = NSCondition() var dataAvailable = false func producer() { condition.lock() // Producción de datos dataAvailable = true condition.signal() // Señala a los hilos en espera condition.unlock() } func consumer() { condition.lock() while !dataAvailable { condition.wait() // Espera hasta que se cumpla la condición } // Procesamiento de datos dataAvailable = false condition.unlock() } -
NSConditionLock: Un mutex que solo puede ser adquirido cuando se cumple una condición-valor específica.
let conditionLock = NSConditionLock(condition: 0) let DATA_READY = 1 func producer() { conditionLock.lock(when: 0) // Adquirir cuando la condición sea 0 // Producción de datos conditionLock.unlock(withCondition: DATA_READY) // Liberar y establecer la condición a DATA_READY } func consumer() { conditionLock.lock(when: DATA_READY) // Adquirir cuando la condición sea DATA_READY // Procesamiento de datos conditionLock.unlock(withCondition: 0) // Liberar y establecer la condición a 0 } -
Dispatch Queues (GCD): Uso de colas de despacho secuenciales para acceder a recursos compartidos.
let serialQueue = DispatchQueue(label: "com.yourapp.serialQueue") var sharedResource = 0 func updateResource() { serialQueue.async { // Acceso a sharedResource solo desde esta cola sharedResource += 1 } } -
NSOperationQueue: Permite controlar dependencias y prioridades de operaciones.
let operationQueue = OperationQueue() var sharedOperationResource = 0 let operation1 = BlockOperation { // Acceso a sharedOperationResource sharedOperationResource += 1 } operationQueue.addOperation(operation1) -
Actors (Swift 5.5+): Aseguran la aislamiento del estado, evitando accesos concurrentes desde diferentes contextos.
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) } -
Contextos de Core Data: Para sincronizar contextos de datos, se pueden usar contextos jerárquicos (padre-hijo) o mergeChanges(fromContextDidSaveNotification:).
// Ejemplo con jerarquía (padre - en segundo plano, hijo - UI) let privateMoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) privateMoc.persistentStoreCoordinator = persistentStoreCoordinator let mainMoc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) mainMoc.parent = privateMoc // Ejemplo 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 { // Si el guardado no ocurrió en el contexto principal mainMoc.perform { mainMoc.mergeChanges(fromContextDidSave: notification) } } } -
Operaciones atómicas: Uso de primitivas atómicas (por ejemplo, de
os/lock.h) para operaciones simples.
La elección del método depende de la complejidad de la tarea, el tipo de datos y los requisitos de rendimiento. Los actores y GCD son enfoques modernos preferidos.