es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Swift evitando ciclos de retención con child viewControllers y uso de singletons

He estado utilizando singletons en exceso y esto ha resultado en fugas de memoria y algunos problemas para gestionar estados. Estoy tratando de entender las alternativas adecuadas al uso de singletons y la inyección de dependencias en general, es decir, los protocolos.

El ejemplo con el que estoy tratando es que MainViewController tiene un paginador que muestra respectivamente ViewControllerA y ViewControllerB. MainViewController tiene un selector que determina el tipo de contenido de ViewControllerA/ViewControllerB. Básicamente, cuando se selecciona un tipo de contenido diferente, necesito recargar los datos del ViewController hijo mostrado. Estoy tratando de averiguar cómo llamar correctamente a reloadData con el ContentType seleccionado en el ViewController hijo. Si creo instancias de los ViewController hijos a nivel de clase y hago algo como esto:

class MainVC: UIViewController {

...

@objc func contentSelectorTapped() {
 ...
 reloadDisplayedChild(prodTypeForIndex(i))
}

func reloadDisplayedChild(_ prodType: ProductType) {
 switch currentPage {
     case .pageA
         pageA.reloadData(prodType)
     case .pageB
         pageB.reloadData(prodType)
 }
}
}

...

class PageAViewController: UIViewController {
...
func reloadData(_ prodType: ProductType) {
 ...
 //Normalmente llamaría a un singleton de capa de datos que creé, pero estoy tratando de alejarme de este enfoque
}
}

Me preocupa que esté creando un ciclo de retención y siento que habrá una gran cantidad de sobrecarga. Y si esto está bien, ¿cómo debo declarar mis instancias de los ViewController hijos para que no creen un ciclo de retención?

lazy var pageAVC: UIPageAViewController = {
 let page = UIPageAVC()
 page.delegate = self
...
 return page
}()

Siento que conceptualmente entiendo cómo evitar los ciclos de retención, pero estoy teniendo dificultades para implementarlo realmente y mantener alguna estructura arquitectónica (en mi caso, MVP).

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Para evitar el uso de singletons y mejorar la inyección de dependencias, puedes utilizar protocolos en su lugar. En tu ejemplo, puedes crear un protocolo al cual los controladores de vista secundarios deben conformarse e implementar el método reloadData.

    protocol ChildViewController {
        func reloadData(_ prodType: ProductType)
    }
    

    Luego, puedes hacer que las clases ViewControllerA y ViewControllerB se adhieran a este protocolo.

    class ViewControllerA: UIViewController, ChildViewController {
        func reloadData(_ prodType: ProductType) {
            // Implementa aquí la lógica para recargar los datos
        }
    }
    
    class ViewControllerB: UIViewController, ChildViewController {
        func reloadData(_ prodType: ProductType) {
            // Implementa aquí la lógica para recargar los datos
        }
    }
    

    En tu MainViewController, declara los controladores de vista secundarios como instancias del tipo del protocolo.

    class MainViewController: UIViewController {
    
        private var pageA: ChildViewController!
        private var pageB: ChildViewController!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Instancia los controladores de vista secundarios
            pageA = ViewControllerA()
            pageB = ViewControllerB()
        }
    
        @objc func contentSelectorTapped() {
             // Obtén el tipo de contenido seleccionado
             let contentType = prodTypeForIndex(i)
    
             // Recarga el controlador de vista secundario mostrado con el tipo de contenido seleccionado
             reloadDisplayedChild(contentType)
        }
    
        func reloadDisplayedChild(_ prodType: ProductType) {
            switch currentPage {
                case .pageA:
                    pageA.reloadData(prodType)
                case .pageB:
                    pageB.reloadData(prodType)
            }
        }
    }
    

    Al declarar los controladores de vista secundarios como instancias del tipo del protocolo, no estás creando un ciclo de retención. El protocolo actúa como un contrato y las instancias de los controladores de vista secundarios solo necesitan conformarse al protocolo para poder ser utilizadas en MainViewController. Esto también permite una prueba y modularidad más fácil en tu código.

Comments are closed.