ProgramaciónDesarrollador iOS

¿Qué son los Property Wrappers en Swift? ¿Cómo funcionan, para qué se utilizan, qué limitaciones y posibilidades existen? Proporcione un ejemplo de creación y aplicación de un property wrapper personalizado.

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Property Wrappers son un mecanismo que permite encapsular la lógica de trabajo con propiedades (por ejemplo, validación, cambios o almacenamiento de una manera específica) y reutilizarla para diferentes propiedades mediante anotaciones en el código. Ayudan a eliminar el código repetido y aumentar la legibilidad.

Un Property Wrapper es una estructura, clase o enum que implementa el protocolo de property wrapper a través de la anotación @propertyWrapper y la propiedad obligatoria wrappedValue.

Limitaciones y detalles:

  • No se pueden aplicar Property Wrappers a propiedades calculadas.
  • La transmisión de argumentos al inicializar el wrapper puede estar limitada.
  • Al usar varias propiedades envueltas dentro de una estructura, solo funciona correctamente con tipos de valor.

Ejemplo: Escribiremos un property wrapper para limitar automáticamente el rango de valores (Clamped).

@propertyWrapper struct Clamped<Value: Comparable> { var value: Value let range: ClosedRange<Value> var wrappedValue: Value { get { value } set { value = min(max(newValue, range.lowerBound), range.upperBound) } } init(wrappedValue initialValue: Value, _ range: ClosedRange<Value>) { self.range = range self.value = min(max(initialValue, range.lowerBound), range.upperBound) } } struct Person { @Clamped(0...120) var age: Int = 25 } var p = Person() p.age = 200 // Ahora p.age = 120 p.age = -10 // Ahora p.age = 0

Pregunta capciosa

¿Qué forma de acceso al objeto de property wrapper original está disponible fuera de la estructura/clase donde se aplica?

Respuesta: A través del nombre de la propiedad con un guion bajo (_). Por ejemplo, si la propiedad se llama age, el objeto de property wrapper se puede obtener como _age:

var p = Person() let wrapper = p._age // este es del tipo Clamped<Int>

Ejemplos de errores reales por desconocer los detalles del tema


Historia

En un proyecto para almacenar UserDefaults se implementó un property wrapper personalizado que trabajaba con tipos primitivos. Al intentar usarlo con tipos de referencia (class) surgió una fuga de memoria inesperada: el property wrapper mantenía una referencia fuerte al objeto, lo que provocaba un ciclo fuerte y una fuga de datos. El error se corrigió pasando a una referencia weak/unowned dentro del wrapper.


Historia

En el proyecto, se intentaron aplicar property wrappers a propiedades calculadas, pero el compilador emitió un error: el property wrapper solo puede ser utilizado con propiedades almacenadas. Este hecho se pasó por alto, lo que retrasó el desarrollo del módulo durante 2 días.


Historia

Al crear la estructura de wrapper, olvidamos implementar la sintaxis correcta de inicialización a través de init(wrappedValue:...). Como resultado, no se podían establecer valores predeterminados a través del property wrapper, lo que solo se reveló después de integrar el wrapper en un gran número de modelos. Tuvimos que revisar la arquitectura.