web-dev-qa-db-fra.com

Comment utiliser objc_setAssociatedObject / objc_getAssociatedObject dans un objet?

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
65
leo

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
62
Brent Priddy

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/ )

38
zlajo

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.

22
ipmcc

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";
16
Nicholas Riley

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); 
}
9
hfossli

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.

5
DarkDust