Si j'utilise objc_setAssociatedObject/objc_getAssociatedObject dans une implémentation de catégorie pour stocker une variable d'instance simulée dans une méthode setter, comment pourrais-je accéder à la clé dans la méthode getter puisque toutes les variables déclarées dans la méthode setter seraient en dehors du champ d'application de la méthode getter?
Edit: Pour clarifier, si je devais utiliser le modèle suivant, où devrais-je déclarer STRING_KEY afin de pouvoir l'utiliser à la fois dans la méthode setter et la méthode getter.
@interface NSView (simulateVar)
-(void)setSimualtedString:(NSString *)myString;
-(NSString *)simulatedString;
@end
@implementation NSView (simulateVar)
-(void)setSimualtedString: (NSString *)myString
{
objc_setAssociatedObject(self, &STRING_KEY, myString, OBJC_ASSOCIATION_RETAIN);
}
-(NSString *)simulatedString
{
return (NSString *)objc_getAssociatedObject(self, &STRING_KEY);
}
@end
Déclarez une variable statique pour pouvoir utiliser son adresse comme clé. L'appel à objc_setAssociatedObject prend un vide * et seule l'adresse de votre variable statique est réellement utilisée, pas le contenu d'une NSString ... qui ne fait que gaspiller de la mémoire.
Il vous suffit d'ajouter:
static char STRING_KEY; // global 0 initialization is fine here, no
// need to change it since the value of the
// variable is not used, just the address
Je sais que cette question est ancienne, mais je pense que pour être complet, il existe une autre façon d'utiliser les objets associés qui mérite d'être mentionnée. Cette solution utilise @selector
et donc aucune variable ou constante supplémentaire n'est nécessaire.
@interface NSObject (CategoryWithProperty)
@property (nonatomic, strong) NSObject *property;
@end
@implementation NSObject (CategoryWithProperty)
- (NSObject *)property {
return objc_getAssociatedObject(self, @selector(property));
}
- (void)setProperty:(NSObject *)value {
objc_setAssociatedObject(self, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
(inspiré par http://www.tuaw.com/2013/04/10/devjuice-better-objective-c-associated-objects/ )
Pour le stockage associé void *
touches, j'aime cette façon de faire:
static void * const kMyAssociatedStorageKey = (void*)&kMyAssociatedStorageKey;
Cela évite d'avoir une autre chaîne constante dans l'exécutable, et en définissant sa valeur à l'adresse de lui-même, vous obtenez un bon uniquing et un comportement const
(vous obtiendrez donc nominalement une plainte du compilateur si vous faites quelque chose qui modifier la valeur de la clé), sans symboles exportés inutiles supplémentaires.
Déclarez une variable statique (unité de portée de compilation) au niveau supérieur du fichier source. Cela peut aider à le rendre significatif, quelque chose comme ceci:
static NSString *MYSimulatedString = @"MYSimulatedString";
Tout près. Voici un exemple complet.
. h-file
@interface NSObject (ExampleCategoryWithProperty)
@property (nonatomic, retain) NSArray *laserUnicorns;
@end
. m-file
#import <objc/runtime.h>
static void * LaserUnicornsPropertyKey = &LaserUnicornsPropertyKey;
@implementation NSObject (ExampleCategoryWithProperty)
- (NSArray *)laserUnicorns {
return objc_getAssociatedObject(self, LaserUnicornsPropertyKey);
}
- (void)setLaserUnicorns:(NSArray *)unicorns {
objc_setAssociatedObject(self, LaserUnicornsPropertyKey, unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
Tout comme une propriété normale - accessible avec la notation par points
NSObject *myObject = [NSObject new];
myObject.laserUnicorns = @[@"dipwit", @"dipshit"];
NSLog(@"Laser unicorns: %@", myObject.laserUnicorns);
Syntaxe plus simple
Vous pouvez également utiliser @selector(nameOfGetter)
au lieu de créer un pointeur statique. Pourquoi? Voir https://stackoverflow.com/a/16020927/202451 . Exemple:
- (NSArray *)laserUnicorns {
return objc_getAssociatedObject(self, @selector(laserUnicorns));
}
- (void)setLaserUnicorns:(NSArray *)unicorns {
objc_setAssociatedObject(self, @selector(laserUnicorns), unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
La réponse acceptée avait déjà raison, mais j'aimerais ajouter une explication: le but de la "clé" est d'obtenir un identifiant unique (par processus/programme) qui n'aura pas de collisions accidentelles.
Imaginez que la clé soit déclarée comme NSUInteger
: beaucoup de gens utiliseraient des valeurs standard comme 0
, 1
ou 42
. Surtout si vous utilisez une bibliothèque ou un framework, des collisions sont probables.
Donc, à la place, certains ingénieux Apple ont eu l'idée de déclarer la clé comme un pointeur avec l'intention de déclarer une variable et de passer le pointeur vers cette variable comme L'éditeur de liens attribue à cette variable une adresse unique dans votre application et vous garantissez ainsi une valeur unique sans collision.
En fait, vous pouvez passer n'importe quelle valeur que vous aimez, le pointeur n'est pas déréférencé (j'ai testé cela). Donc en passant (void *)0
ou (void *)42
car la clé fonctionne, bien que ce ne soit pas une bonne idée (donc ne faites pas ça). Pour objc_set/getAssociatedObject
, tout ce qui compte, c'est que la valeur transmise en tant que clé soit unique dans votre processus/programme.