DEV Community

GoyesDev
GoyesDev

Posted on

[SC] Manejo de Memoria en Swift Concurrency

Comprensión durante la lectura

¿Qué es Swift Concurrency y por qué es relevante para el manejo de memoria?

Swift Concurrency es un modelo de concurrencia de Swift que se basa en Tasks para ejecutar trabajo de forma asíncrona.

Es relevante para el manejo de memoria porque las Tasks puede retener instancias y provocar comportamientos inesperados debido a su efecto retardado.

¿De qué manera capturan variables las Task en Swift Concurrency, y en qué se parecen a los closures normales?

Un Task puede retener una referencia de forma fuerte tanto de forma implícita como explícita (con el "capture list").

¿Por qué la concurrencia puede ocultar o agravar problemas de memoria en comparación con código síncrono?

Una Task podría estar reteniendo un objeto que, en cierto momento se asume que ya fue liberado. Sin embargo, debido a su naturaleza asíncrona, el efecto de haber retenido dicho objeto puede aparecer más tarde, y esto puede ser difícil de rastrear.

¿Qué sucede con un objeto cuando una Task captura self de forma fuerte (strong reference) y el objeto se establece en nil antes de que la tarea termine?

El objeto se libera después de que la Task termina.

¿Cuál es la diferencia en el comportamiento del ciclo de vida del objeto cuando se usa [weak self] dentro de una Task?

Al usar [weak self], ARC puede liberar self durante la suspensión del Task. Cuando la tarea reanuda después de await, self puede ser nil por lo que las llamadas posteriores a self no se ejecutan.

¿Qué significa que los actores (actors) gestionan su memoria de forma independiente y serializan el acceso a sus propiedades?

Los actores sincronizan el acceso a su memoria a través de un executor. De este modo, todo cliente que quiera modificar una propiedad tendrá que hacerlo a través de un método accesor async, y se puede leer directamente accediendo con await a la propiedad.

Según el artículo, ¿en qué orden aparecen los mensajes impresos cuando fetchData() captura self fuertemente y el viewModel se pone en nil? ¿Por qué ocurre ese orden?

@MainActor
final class ContentViewModel {
  deinit {
    print("Deinit!")
  }

  func fetchData() {
    Task {
      await performNetworkRequest()
      updateUI() // ⚠️ Captures self strongly!
    }
  }

  func updateUI() {
    print("Update UI!")
  }

  func performNetworkRequest() async {
    print("Perform network request")
    try? await Task.sleep(for: .seconds(1))
    print("Finished network request")
  }
}

var viewModel: ContentViewModel? = .init()
viewModel?.fetchData()
viewModel = nil
print("Set viewModel to nil")
Enter fullscreen mode Exit fullscreen mode

Imprime:

// Perform network request
// Set viewModel to nil
// Finished network request
// Update UI!
// Deinit!
Enter fullscreen mode Exit fullscreen mode

El deinit se llama al final, incluso aunque se puso viewModel = nil porque la Task retuvo el objeto.

¿Qué ocurre con performNetworkRequest() cuando se usa [weak self] y el objeto se libera a mitad de la ejecución?

La petición simulada termina después de que se libera el objeto en cuestión. Luego, cuando el flujo de control regresa al Task, las instrucciones siguientes no se ejecutan porque self es nil.


Recordar sin releer

Sin mirar el artículo, ¿puedes explicar con tus propias palabras por qué una Task puede mantener vivo un objeto más tiempo del esperado?

¿Cuáles son las dos formas de capturar self dentro de una Task que muestra el artículo, y cuál es la consecuencia de cada una?


Revisión y síntesis

¿Cuál es la conclusión principal del artículo sobre el manejo de memoria en Swift Concurrency respecto al código normal?

¿Qué impacto práctico puede tener una retención prolongada de objetos en el rendimiento de una aplicación?

¿Qué temas o ejemplos adicionales anticipa el artículo que se cubrirán en la siguiente lección?


Bibliografía

Top comments (0)