Estoy cambiando una aplicación de Objective-C a Swift, que tengo un par de categorías con propiedades almacenadas, por ejemplo:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Como las extensiones Swift no aceptan propiedades almacenadas como estas, no sé cómo mantener la misma estructura que el código Objc. Las propiedades almacenadas son realmente importantes para mi aplicación y creo que Apple debe haber creado alguna solución para hacerlo en Swift.
Como dijo jou, lo que estaba buscando era usar objetos asociados, así que lo hice (en otro contexto):
import Foundation
import QuartzCore
import ObjectiveC
extension CALayer {
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
}
set(newValue) {
objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
var initialPath: CGPathRef! {
get {
return objc_getAssociatedObject(self, "initialPath") as CGPathRef
}
set {
objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
Pero obtengo un EXC_BAD_ACCESS cuando hago:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
¿Algunas ideas?
ios
swift
associated-object
Marcos Duarte
fuente
fuente
Respuestas:
La API de objetos asociados es un poco engorrosa de usar. Puede eliminar la mayor parte del repetitivo con una clase auxiliar.
Siempre que pueda "agregar" una propiedad a la clase objetiva-c de una manera más legible:
En cuanto a la solución:
fuente
NSNumber
oyNSValue
escribir pares adicionales de accesos que serían del tipo que desea (Int, Bool, etc.).NSObjectProtocol
[String]?
es una estructura, este es el mismo caso que para Int, Bool. Puede usarNSArray
para mantener una colección deNSString
instancias.Como en Objective-C, no puede agregar propiedades almacenadas a clases existentes. Si está extendiendo una clase Objective-C (
UIView
definitivamente es una), aún puede usar objetos asociados para emular propiedades almacenadas:para Swift 1
La clave de asociación es un puntero que debe ser único para cada asociación. Para eso, creamos una variable global privada y utilizamos su dirección de memoria como clave con el
&
operador. Consulte Uso de Swift con Cocoa y Objective-C para obtener más detalles sobre cómo se manejan los punteros en Swift.ACTUALIZADO para Swift 2 y 3
ACTUALIZADO para Swift 4
En Swift 4, es mucho más simple. La estructura Holder contendrá el valor privado que nuestra propiedad computada expondrá al mundo, dando la ilusión de un comportamiento de propiedad almacenada.
Fuente
fuente
objc_AssociationPolicy
, gracias! He actualizado la respuestaAsí que creo que encontré un método que funciona más limpio que los anteriores porque no requiere ninguna variable global. Lo obtuve desde aquí: http://nshipster.com/swift-objc-runtime/
La esencia es que usa una estructura como esta:
ACTUALIZACIÓN para Swift 2
fuente
La solución señalada por jou no admite tipos de valor , esto también funciona bien con ellos
Envolturas
Una posible extensión de clase (ejemplo de uso):
Esta es realmente una gran solución, quería agregar otro ejemplo de uso que incluye estructuras y valores que no son opcionales. Además, los valores de AssociatedKey se pueden simplificar.
fuente
lift
método y laLifted
clase, deberían usar sin embargogetAssociatedObject
&setAssociatedObject
funciones. Agregaré "Ejemplo de uso" entre paréntesis junto al encabezado en aras de la claridad.No puede definir categorías (extensiones Swift) con nuevo almacenamiento; cualquier propiedad adicional debe calcularse en lugar de almacenarse. La sintaxis funciona para el objetivo C porque
@property
en una categoría significa esencialmente "proporcionaré el captador y el definidor". En Swift, deberá definirlos usted mismo para obtener una propiedad calculada; algo como:Debería funcionar bien. Recuerde, no puede almacenar nuevos valores en el setter, solo funciona con el estado de clase disponible existente.
fuente
Mis $ 0.02. Este código está escrito en Swift 2.0
He intentado muchas soluciones, y descubrí que esta es la única forma de extender realmente una clase con parámetros variables adicionales.
fuente
type 'AssociatedKeys' cannot be defined within a protocol extension
...¿Por qué confiar en objc runtime? No entiendo el punto. Al usar algo como lo siguiente, logrará el comportamiento casi idéntico de una propiedad almacenada, al usar solo un enfoque puro de Swift :
fuente
Prefiero hacer código en Swift puro y no confiar en la herencia de Objective-C. Debido a esto, escribí una solución Swift pura con dos ventajas y dos desventajas.
Ventajas:
Código Swift puro
Funciona en clases y terminaciones o más específicamente en
Any
objetos.Desventajas
El código debe llamar al método
willDeinit()
para liberar objetos vinculados a una instancia de clase específica para evitar pérdidas de memoriaNo puede hacer una extensión directamente a UIView para este ejemplo exacto porque
var frame
es una extensión a UIView, no parte de la clase.EDITAR:
fuente
extensionPropertyStorage
se comparte con todas las instancias por diseño. Es la variable global (Diccionario) la que primero desreferencia la instancia de UILabel (NSObject
) y luego su propiedad ([String: Any]
).class
propiedades;)frame
es una propiedad de instancia ¿De dónde viene la propiedad de clase?Con las categorías Obj-c solo puede agregar métodos, no variables de instancia.
En su ejemplo, ha utilizado @property como acceso directo para agregar declaraciones de métodos getter y setter. Aún necesita implementar esos métodos.
De manera similar, en Swift puede agregar extensiones de uso para agregar métodos de instancia, propiedades calculadas, etc. pero no propiedades almacenadas.
fuente
También recibo un problema EXC_BAD_ACCESS. El valor
objc_getAssociatedObject()
yobjc_setAssociatedObject()
debe ser un objeto. Y elobjc_AssociationPolicy
debe coincidir con el objeto.fuente
Intenté usar objc_setAssociatedObject como se menciona en algunas de las respuestas aquí, pero después de fallar algunas veces, di un paso atrás y me di cuenta de que no hay razón para eso. Partiendo de algunas de las ideas aquí, se me ocurrió este código que simplemente almacena una matriz de lo que mis datos adicionales están (MyClass en este ejemplo) indexados por el objeto con el que quiero asociarlos:
fuente
Aquí hay una solución simplificada y más expresiva. Funciona tanto para el valor como para los tipos de referencia. El enfoque del levantamiento se toma de la respuesta de @HepaKKes.
Código de asociación:
Ejemplo de uso:
1) Crear extensión y asociarle propiedades. Usemos tanto el valor como las propiedades de tipo de referencia.
2) Ahora puedes usar las propiedades de forma regular:
Más detalles:
fuente
Otro ejemplo con el uso de objetos asociados Objective-C y propiedades calculadas para Swift 3 y Swift 4
fuente
si está buscando establecer un atributo de cadena personalizado en una UIView, así es como lo hice en Swift 4
Crear una extensión UIView
Para usar esta extensión
fuente
¿Qué hay de almacenar el mapa estático en la clase que se extiende así:
fuente
Traté de almacenar propiedades utilizando objc_getAssociatedObject, objc_setAssociatedObject, sin suerte. Mi objetivo era crear una extensión para UITextField, para validar la longitud de los caracteres de entrada de texto. El siguiente código funciona bien para mí. Espero que esto ayude a alguien.
fuente
_min
y_max
son globales y serán lo mismo en todas las instancias del UITextField? Incluso si funciona para usted, esta respuesta no está relacionada porque Marcos pregunta por las variables de instancia .Aquí hay una alternativa que funciona también
fuente
Esta solución me pareció más práctica
ACTUALIZADO para Swift 3
... luego en tu código
fuente