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.

Cómo inyectar código personalizado en cada instancia de una clase incorporada en Swift?

Necesito ejecutar algún código en la instanciación de cada UILabel en mi aplicación. Con fines de depuración/investigación.

Entonces, me gustaría hacer algo como esto:

// Yep, no es posible sobrescribir en una extensión
extension UILabel {
    override init(...) { // ¿Y los argumentos? ¿Sobrecarga? No tengo idea
        super.init(...)
        print("Se creó un UILabel")
    }
}

o algo así:

// Sí, no es una sintaxis válida

class MyLabel : UILabel {
    override init(...) { // ¿Y los argumentos? ¿Sobrecarga? No tengo idea
        super.init(...)
        print("Se creó un UILabel")
    }
}

Ambos ejemplos anteriores no son un código Swift válido, pero espero que haya alguna solución correcta para inyectar mi código en la instanciación de cada UILabel.

No tiene que ser una solución limpia. El código nunca saldrá de mi compilación local de depuración, por lo que cualquier truco sucio es totalmente aceptable siempre que haga el trabajo.

Gracias.

Tags:  ,

Answer

  1. Avatar for davy.ai

    Una manera de lograr esto sería crear una subclase de UILabel y anular el método init. Luego, en lugar de usar UILabel en tu aplicación, usarías tu subclase personalizada. Aquí tienes un ejemplo:

    class MiLabel: UILabel {
        override init(frame: CGRect) {
            super.init(frame: frame)
            print("Se creó UILabel")
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            print("Se creó UILabel desde el storyboard o la interfaz de usuario")
        }
    }
    

    Entonces, cada vez que quieras usar un UILabel, usarías MiLabel en su lugar, de la siguiente manera:

    let miLabel = MiLabel(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
    

    Alternativamente, si deseas evitar tener que reemplazar cada aparición de UILabel en tu aplicación, podrías usar “method swizzling” para inyectar tu código en el método init de UILabel. “Method swizzling” es una técnica que te permite reemplazar la implementación de un método en tiempo de ejecución. Aquí te muestro cómo hacerlo:

    extension UILabel {
        private static let swizzleOnce: Void = {
            let originalSelector = #selector(UILabel.init(frame:))
            let swizzledSelector = #selector(UILabel.swizzledInit(frame:))
            let originalMethod = class_getInstanceMethod(UILabel.self, originalSelector)!
            let swizzledMethod = class_getInstanceMethod(UILabel.self, swizzledSelector)!
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }()
    
        @objc func swizzledInit(frame: CGRect) {
            // Llama primero a la implementación original
            swizzledInit(frame: frame)
    
            // Injecta tu código aquí
            print("Se creó UILabel")
        }
    }
    
    // Llama a este método una vez al iniciar la aplicación para hacer "method swizzling" en el método init de UILabel
    UILabel.swizzleOnce
    

    Este código define un método estático llamado swizzleOnce que usa “method swizzling” para reemplazar la implementación de init(frame:) en UILabel con una nueva implementación que llama primero a la implementación original y luego inyecta tu código de depuración. El método swizzledInit es la nueva implementación que proporcionas.

    Debes llamar a UILabel.swizzleOnce una vez al iniciar la aplicación para hacer “method swizzling” en el método init de UILabel.

    Ten en cuenta que “method swizzling” puede ser peligroso si no se hace correctamente, así que úsalo con precaución y pruébalo a fondo.

Comments are closed.