La concurrència és un concepte fonamental en la programació moderna que permet executar múltiples tasques al mateix temps, millorant així l'eficiència i la resposta de les aplicacions. En Swift, la concurrència es gestiona principalment mitjançant Grand Central Dispatch (GCD) i operacions. Aquest mòdul t'introduirà als conceptes bàsics de la concurrència en Swift i et mostrarà com utilitzar-los en les teves aplicacions.

Objectius d'Aprenentatge

  • Comprendre els conceptes bàsics de la concurrència.
  • Aprendre a utilitzar Grand Central Dispatch (GCD) per gestionar tasques concurrents.
  • Utilitzar operacions per gestionar tasques concurrents de manera més controlada.
  • Implementar concurrència segura per evitar condicions de carrera i altres problemes comuns.

Conceptes Bàsics de la Concurrència

Què és la Concurrència?

La concurrència permet que múltiples tasques es realitzin al mateix temps. Això és especialment útil en aplicacions que necessiten realitzar operacions intensives en temps, com ara descàrregues de xarxa, processament d'imatges o càlculs complexos, sense bloquejar la interfície d'usuari.

Multithreading

El multithreading és una tècnica de programació que permet executar múltiples fils (threads) dins d'un mateix procés. Cada fil pot executar una tasca diferent de manera concurrent.

Grand Central Dispatch (GCD)

GCD és una tecnologia d'Apple que facilita la gestió de tasques concurrents. GCD utilitza cues (queues) per gestionar l'execució de tasques de manera eficient.

Utilitzant Grand Central Dispatch (GCD)

Cues (Queues)

GCD utilitza cues per gestionar l'execució de tasques. Hi ha dos tipus principals de cues:

  • Cues Serials: Executen una tasca a la vegada en l'ordre en què es van afegir.
  • Cues Concurrent: Permeten l'execució de múltiples tasques al mateix temps.

Exemple de Cues Serials

let serialQueue = DispatchQueue(label: "com.example.serialQueue")

serialQueue.async {
    print("Tasca 1")
}

serialQueue.async {
    print("Tasca 2")
}

Exemple de Cues Concurrent

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

concurrentQueue.async {
    print("Tasca 1")
}

concurrentQueue.async {
    print("Tasca 2")
}

Tasques Asíncrones i Síncrones

  • Asíncrones (async): Les tasques s'executen en segon pla i el control retorna immediatament.
  • Síncrones (sync): Les tasques s'executen i el control no retorna fins que la tasca ha finalitzat.

Exemple de Tasca Asíncrona

DispatchQueue.global().async {
    print("Aquesta és una tasca asíncrona")
}

Exemple de Tasca Síncrona

DispatchQueue.global().sync {
    print("Aquesta és una tasca síncrona")
}

Utilitzant Operacions

Operacions i Cues d'Operacions

Les operacions proporcionen una manera més controlada de gestionar tasques concurrents. Pots crear subclasses de Operation per definir tasques personalitzades.

Exemple de Cues d'Operacions

let operationQueue = OperationQueue()

let operation1 = BlockOperation {
    print("Operació 1")
}

let operation2 = BlockOperation {
    print("Operació 2")
}

operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)

Dependències entre Operacions

Pots establir dependències entre operacions per assegurar-te que es compleixin en un ordre específic.

Exemple de Dependències

let operation1 = BlockOperation {
    print("Operació 1")
}

let operation2 = BlockOperation {
    print("Operació 2")
}

operation2.addDependency(operation1)

let operationQueue = OperationQueue()
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)

Concurrència Segura

Condicions de Carrera

Les condicions de carrera ocorren quan múltiples fils accedeixen i modifiquen la mateixa dada al mateix temps. Això pot causar comportaments inesperats.

Utilitzant Barreres

Les barreres permeten assegurar que només una tasca accedeixi a una dada compartida a la vegada.

Exemple de Barrera

let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

concurrentQueue.async(flags: .barrier) {
    // Codi que modifica dades compartides
}

Utilitzant Semàfors

Els semàfors controlen l'accés a recursos compartits limitant el nombre de fils que poden accedir-hi al mateix temps.

Exemple de Semàfor

let semaphore = DispatchSemaphore(value: 1)

DispatchQueue.global().async {
    semaphore.wait()
    // Codi que accedeix a dades compartides
    semaphore.signal()
}

Exercicis Pràctics

Exercici 1: Utilitzant GCD

Crea una aplicació que descarregui una imatge des d'una URL en segon pla i la mostri en una UIImageView a la interfície d'usuari.

Solució

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        downloadImage()
    }

    func downloadImage() {
        let url = URL(string: "https://example.com/image.jpg")!
        DispatchQueue.global().async {
            if let data = try? Data(contentsOf: url) {
                DispatchQueue.main.async {
                    self.imageView.image = UIImage(data: data)
                }
            }
        }
    }
}

Exercici 2: Utilitzant Operacions

Crea una aplicació que realitzi tres operacions en segon pla: descarregar dades, processar-les i actualitzar la interfície d'usuari. Assegura't que les operacions es compleixin en l'ordre correcte.

Solució

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        performOperations()
    }

    func performOperations() {
        let operationQueue = OperationQueue()

        let downloadOperation = BlockOperation {
            // Simula la descàrrega de dades
            sleep(2)
            print("Dades descarregades")
        }

        let processOperation = BlockOperation {
            // Simula el processament de dades
            sleep(2)
            print("Dades processades")
        }

        let updateUIOperation = BlockOperation {
            // Actualitza la interfície d'usuari
            DispatchQueue.main.async {
                self.label.text = "Operacions completades"
            }
        }

        processOperation.addDependency(downloadOperation)
        updateUIOperation.addDependency(processOperation)

        operationQueue.addOperations([downloadOperation, processOperation, updateUIOperation], waitUntilFinished: false)
    }
}

Resum

En aquest mòdul, has après els conceptes bàsics de la concurrència en Swift, incloent-hi l'ús de Grand Central Dispatch (GCD) i operacions per gestionar tasques concurrents. També has après a implementar concurrència segura per evitar condicions de carrera i altres problemes comuns. La concurrència és una eina poderosa que pot millorar significativament el rendiment i la resposta de les teves aplicacions, però cal utilitzar-la amb cura per evitar errors.

En el següent mòdul, explorarem el Gestor de Paquets Swift, una eina que et permetrà gestionar les dependències del teu projecte de manera eficient.

© Copyright 2024. Tots els drets reservats