web-dev-qa-db-fra.com

Qu'est-ce que objc_setAssociatedObject () et dans quels cas faut-il l'utiliser?

Dans un projet que j'ai entrepris, l'auteur d'origine a choisi d'utiliser objc_setAssociatedObject() et je ne sais pas à 100% ce qu'il fait ni pourquoi il a décidé de l'utiliser.

J'ai décidé de le rechercher et, malheureusement, les documents ne sont pas très descriptifs sur son objectif.

objc_setAssociatedObject
Définit une valeur associée pour un objet donné à l'aide d'une clé et d'une stratégie d'association données.
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
Paramètres
object
L'objet source de l'association.
key
La clé de l'association.
value
La valeur à associer à la touche clé de l'objet. Passez à zéro pour effacer une association existante.
policy
La politique de l'association. Pour les valeurs possibles, voir "Comportements des objets associatifs".

Alors, que fait exactement cette fonction et dans quels cas doit-elle être utilisée?


Modifier après avoir lu les réponses

Quel est donc le point dans le code suivant?

Device *device = [self.list objectAtIndex:[indexPath row]];
DeviceViewController *next = [[DeviceViewController alloc] initWithController:self.controller
                                                                            device:device
                                                                               item:self.rootVC.selectedItem];  
    objc_setAssociatedObject(device, &kDeviceControllerKey, next, OBJC_ASSOCIATION_RETAIN);

Quel est l'intérêt d'associer le périphérique au contrôleur de vue s'il s'agit déjà d'une variable d'instance?

66
Jasarien

A partir des documents de référence sur Objective-C Runtime Reference :

Vous utilisez la fonction d'exécution Objective-C objc_setAssociatedObject pour faire une association entre un objet et un autre. La fonction prend quatre paramètres: l'objet source, une clé, la valeur et une constante de stratégie d'association. La clé est un pointeur vide.

  • La clé de chaque association doit être unique. Un modèle typique consiste à utiliser une variable statique.
  • La politique spécifie si l'objet associé est affecté,
    conservé ou copié, et si le
    l'association doit être faite par voie atomique ou
    non atomique. Ce modèle est
    similaire à celle des attributs de
    une propriété déclarée (voir "Propriété
    Attributs de déclaration "). Vous spécifiez la stratégie de la relation à l'aide d'une constante (voir
    objc_AssociationPolicy et
    Comportements des objets associatifs).

Établir une association entre un tableau et une chaîne

static char overviewKey;



NSArray *array =

    [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];

// For the purposes of illustration, use initWithFormat: to ensure

// the string can be deallocated

NSString *overview =

    [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];



objc_setAssociatedObject (

    array,

    &overviewKey,

    overview,

    OBJC_ASSOCIATION_RETAIN

);



[overview release];

// (1) overview valid

[array release];

// (2) overview invalid

Au point 1, la vue d'ensemble des chaînes est toujours valide car la stratégie OBJC_ASSOCIATION_RETAIN spécifie que le tableau conserve l'objet associé. Cependant, lorsque le tableau est désalloué (au point 2), la vue d'ensemble est libérée et, dans ce cas, également désallouée. Si vous essayez, par exemple, de consigner la valeur de la vue d'ensemble, vous générez une exception d'exécution.

33
visakh7

objc_setAssociatedObject ajoute un magasin de valeurs clés à chaque objet Objective-C. Il vous permet de stocker un état supplémentaire pour l'objet, non reflété dans ses variables d'instance.

C'est vraiment pratique lorsque vous souhaitez stocker des objets appartenant à un objet en dehors de l'implémentation principale. L'un des principaux cas d'utilisation se trouve dans des catégories où vous ne pouvez pas ajouter de variables d'instance. Ici, vous utilisez objc_setAssociatedObject pour attacher vos variables supplémentaires à l'objet self.

Lorsque vous utilisez la bonne stratégie d'association, vos objets seront libérés lorsque l'objet principal sera désalloué.

58
Nikolai Ruhe

Voici une liste de cas d'utilisation pour les associations d'objets:

un: Pour ajouter des variables d'instance aux catégories. En général, cette technique est conseillée contre, mais voici un exemple d'une utilisation légitime. Supposons que vous souhaitiez simuler des variables d'instance supplémentaires pour des objets que vous ne pouvez pas modifier (nous parlons de modifier l'objet lui-même, c'est-à-dire sans sous-classement). Disons que définir un titre sur un UIImage.

// UIImage-Title.h:
@interface UIImage(Title)
@property(nonatomic, copy) NSString *title;
@end 

// UIImage-Title.m:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

static char titleKey;

@implementation UIImage(Title)
- (NSString *)title
{
    return objc_getAssociatedObject(self, &titleKey);
}

- (void)setTitle:(NSString *)title
{
    objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY);
}
@end

En outre, ici est une manière assez complexe (mais géniale) d'utiliser des objets associés avec des catégories .. il vous permet essentiellement de passer un bloc au lieu d'un sélecteur à un UIControl.


deux: Ajout dynamique d'informations d'état à un objet non couvert par ses variables d'instance en conjonction avec KVO.

L'idée est que votre objet obtient des informations d'état uniquement pendant l'exécution (c'est-à-dire dynamiquement). L'idée est donc que bien que vous puissiez stocker ces informations d'état dans une variable d'instance, le fait que vous attachez ces informations à un objet instancié au moment de l'exécution et que vous les associez dynamiquement à l'autre objet, vous mettez en évidence le fait qu'il s'agit un état dynamique de l'objet.

Un excellent exemple qui illustre cela est la bibliothèque this , dans laquelle les objets associatifs sont utilisés avec les notifications KVO . Voici un extrait du code (note: cette notification KVO n'est pas nécessaire pour exécuter faire fonctionner le code dans cette bibliothèque .. plutôt il est mis là par l'auteur pour plus de commodité, fondamentalement tout objet qui s'enregistre sera notifié via KVO que des changements lui sont arrivés):

static char BOOLRevealing;

- (BOOL)isRevealing
{
    return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue];
} 

- (void)_setRevealing:(BOOL)revealing
{
    [self willChangeValueForKey:@"isRevealing"];
    objc_setAssociatedObject(self, &BOOLRevealing, 
       [NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"isRevealing"];
}

bonus: jetez un œil à cette discussion/explication des objets associés par Mattt Thompson, auteur de la bibliothèque séminale AFNetworking

25
abbood

Pour répondre à votre question révisée:

Quel est l'intérêt d'associer le périphérique au contrôleur de vue s'il s'agit déjà d'une variable d'instance?

Il y a plusieurs raisons pour lesquelles vous pourriez vouloir faire cela.

  • la classe Device n'a pas de variable ou de propriété d'instance de contrôleur et vous ne pouvez pas la modifier ou la sous-classer, par exemple vous n'avez pas le code source.
  • vous voulez deux contrôleurs associés à l'objet périphérique et vous ne pouvez pas modifier la classe de périphérique ou la sous-classe.

Personnellement, je pense qu'il est très rare d'avoir besoin d'utiliser des fonctions d'exécution Objective-C de bas niveau. Cela ressemble à une odeur de code pour moi.

5
JeremyP