Je passe d'une application d'Objective-C à Swift, dont j'ai quelques catégories avec des propriétés stockées, par exemple:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Comme les extensions Swift n'acceptent pas les propriétés stockées comme celles-ci, je ne sais pas comment maintenir la même structure que le code Objc. Les propriétés stockées sont vraiment importantes pour mon application et je pense qu'Apple a dû créer une solution pour le faire dans Swift.
Comme l'a dit jou, ce que je cherchais était en fait d'utiliser des objets associés, alors j'ai fait (dans un autre contexte):
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))
}
}
}
Mais j'obtiens un EXC_BAD_ACCESS en faisant:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Des idées?
ios
swift
associated-object
Marcos Duarte
la source
la source
Réponses:
L'API des objets associés est un peu lourde à utiliser. Vous pouvez supprimer la plupart des passe-partout avec une classe d'assistance.
À condition que vous puissiez "ajouter" une propriété à la classe objective-c d'une manière plus lisible:
Quant à la solution:
la source
NSNumber
ouNSValue
et écrire des paires d'accesseurs supplémentaires qui seraient des types souhaités (Int, Bool, etc.).NSObjectProtocol
[String]?
est une structure, c'est le même cas que pour Int, Bool. Vous pouvez utiliserNSArray
pour conserver une collection d'NSString
instances.Comme dans Objective-C, vous ne pouvez pas ajouter de propriété stockée aux classes existantes. Si vous étendez une classe Objective-C (en
UIView
est certainement une), vous pouvez toujours utiliser les objets associés pour émuler les propriétés stockées:pour Swift 1
La clé d'association est un pointeur qui doit être l'unique pour chaque association. Pour cela, nous créons une variable globale privée et utilisons son adresse mémoire comme clé avec l'
&
opérateur. Voir Utilisation de Swift avec Cocoa et Objective-C pour plus de détails sur la manière dont les pointeurs sont gérés dans Swift.MISE À JOUR pour Swift 2 et 3
MISE À JOUR pour Swift 4
Dans Swift 4, c'est beaucoup plus simple. La structure Holder contiendra la valeur privée que notre propriété calculée exposera au monde, donnant l'illusion d'un comportement de propriété stockée à la place.
La source
la source
objc_AssociationPolicy
, merci! J'ai mis à jour la réponseJe pense donc avoir trouvé une méthode plus propre que celles ci-dessus car elle ne nécessite aucune variable globale. Je l'ai obtenu d'ici: http://nshipster.com/swift-objc-runtime/
L'essentiel est que vous utilisez une structure comme ceci:
MISE À JOUR pour Swift 2
la source
La solution indiquée par jou ne prend pas en charge les types valeur , cela fonctionne bien avec eux aussi
Emballages
Une extension de classe possible (exemple d'utilisation):
C'est vraiment une excellente solution, je voulais ajouter un autre exemple d'utilisation qui incluait des structures et des valeurs qui ne sont pas optionnelles. En outre, les valeurs AssociatedKey peuvent être simplifiées.
la source
lift
méthode et laLifted
classe, ils devraient cependant utilisergetAssociatedObject
&setAssociatedObject
functions. J'ajouterai "Exemple d'utilisation" entre parenthèses à côté de l'en-tête par souci de clarté.Vous ne pouvez pas définir de catégories (extensions Swift) avec un nouveau stockage; toutes les propriétés supplémentaires doivent être calculées plutôt que stockées. La syntaxe fonctionne pour Objective C car
@property
dans une catégorie signifie essentiellement "Je fournirai le getter et le setter". Dans Swift, vous devrez les définir vous-même pour obtenir une propriété calculée; quelque chose comme:Cela devrait fonctionner correctement. N'oubliez pas que vous ne pouvez pas stocker de nouvelles valeurs dans le setter, ne travaillez qu'avec l'état de classe disponible existant.
la source
Mon 0,02 $. Ce code est écrit en Swift 2.0
J'ai essayé de nombreuses solutions et j'ai trouvé que c'était le seul moyen d'étendre réellement une classe avec des paramètres variables supplémentaires.
la source
type 'AssociatedKeys' cannot be defined within a protocol extension
Pourquoi s'appuyer sur le runtime objc? Je ne comprends pas. En utilisant quelque chose comme ce qui suit, vous obtiendrez presque le même comportement d'une propriété stockée, en utilisant uniquement une approche Swift pure :
la source
Je préfère faire du code en Swift pur et ne pas me fier à l'héritage Objective-C. Pour cette raison, j'ai écrit une solution Swift pure avec deux avantages et deux inconvénients.
Avantages:
Code Swift pur
Fonctionne sur les classes et les complétions ou plus spécifiquement sur l'
Any
objetDésavantages:
Le code doit appeler une méthode
willDeinit()
pour libérer des objets liés à une instance de classe spécifique pour éviter les fuites de mémoireVous ne pouvez pas faire d'extension directement à UIView pour cet exemple exact car cette
var frame
extension à UIView ne fait pas partie d'une classe.ÉDITER:
la source
extensionPropertyStorage
est partagé avec toutes les instances par conception. C'est la variable globale (Dictionary) qui dé-référence d'abord l'instance UILabel (NSObject
) puis sa propriété ([String: Any]
).class
propriétés;)frame
est une propriété d'instance. D'où vient la propriété de classe?Avec les catégories Obj-c, vous ne pouvez ajouter que des méthodes, pas des variables d'instance.
Dans votre exemple, vous avez utilisé @property comme raccourci pour ajouter des déclarations de méthode getter et setter. Vous devez toujours implémenter ces méthodes.
De même, dans Swift, vous pouvez ajouter des extensions d'utilisation pour ajouter des méthodes d'instance, des propriétés calculées, etc. mais pas des propriétés stockées.
la source
J'obtiens également un problème EXC_BAD_ACCESS. La valeur dans
objc_getAssociatedObject()
etobjc_setAssociatedObject()
devrait être un objet. Et leobjc_AssociationPolicy
devrait correspondre à l'objet.la source
J'ai essayé d'utiliser objc_setAssociatedObject comme mentionné dans quelques-unes des réponses ici, mais après avoir échoué plusieurs fois, j'ai pris du recul et j'ai réalisé qu'il n'y avait aucune raison pour laquelle j'en avais besoin. En empruntant à quelques-unes des idées ici, j'ai trouvé ce code qui stocke simplement un tableau de toutes mes données supplémentaires (MyClass dans cet exemple) indexées par l'objet auquel je veux l'associer:
la source
Voici une solution simplifiée et plus expressive. Cela fonctionne pour les types valeur et référence. L'approche du levage est tirée de la réponse @HepaKKes.
Code d'association:
Exemple d'utilisation:
1) Créez une extension et associez-y des propriétés. Utilisons à la fois les propriétés de type valeur et référence.
2) Vous pouvez maintenant utiliser tout comme les propriétés normales:
Plus de détails:
la source
Un autre exemple d'utilisation des objets associés Objective-C et des propriétés calculées pour Swift 3 et Swift 4
la source
si vous cherchez à définir un attribut de chaîne personnalisé sur un UIView, voici comment je l'ai fait sur Swift 4
Créer une extension UIView
Pour utiliser cette extension
la source
Que diriez-vous de stocker une carte statique dans une classe qui s'étend comme ceci:
la source
J'ai essayé de stocker des propriétés en utilisant objc_getAssociatedObject, objc_setAssociatedObject, sans aucune chance. Mon objectif était de créer une extension pour UITextField, pour valider la longueur des caractères d'entrée de texte. Le code suivant fonctionne très bien pour moi. J'espère que cela aidera quelqu'un.
la source
_min
et_max
sont globaux et seront les mêmes dans toutes les instances de UITextField? Même si cela fonctionne pour vous, cette réponse n'est pas liée car Marcos pose des questions sur les variables d' instance .Voici une alternative qui fonctionne aussi
la source
J'ai trouvé cette solution plus pratique
MISE À JOUR pour Swift 3
... puis dans votre code
la source