¿Qué es objc setAssociatedObject() y en qué casos debería usarse?


En un proyecto que he asumido, el autor original ha optado por usar objc_setAssociatedObject() y no estoy 100% claro qué hace o por qué decidieron usarlo.

Decidí buscarlo y, desafortunadamente, los documentos no son muy descriptivos sobre su propósito.

Objc_setAssociatedObject
Establece un valor asociado para un objeto determinado mediante una clave y una política de asociación determinadas.
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
Parámetros
object
El objeto de origen para el asociación.
key
La clave para la asociación.
value
El valor a asociar con la clave clave para el objeto. Pase nil para borrar una asociación existente.
policy
La política de la asociación. Para ver los posibles valores, consulte " Comportamientos de objetos asociativos."

Entonces, ¿qué hace exactamente esta función y en qué casos debe usarse?


Editar después de leer las respuestas

Entonces, ¿cuál es el punto en lo siguiente código?

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

¿De qué sirve asociar el dispositivo con el controlador de vista si ya es una variable de instancia?

Author: Jasarien, 2011-05-06

4 answers

De los documentos de referencia sobre Referencia del tiempo de ejecución de Objective-C :

Se utiliza el tiempo de ejecución de Objective-C función objc_setAssociatedObject a hacer una asociación entre un objeto y otro. La función toma cuatro parámetros: el objeto de origen, una clave, el valor, y una política de asociación constante. La clave es un puntero vacío.

  • La clave para cada asociación debe ser única. Un patrón típico es utilice una variable estática.
  • La política especifica si se asigna el objeto asociado,
    retenido o copiado, y si el
    la asociación se hace atómicamente o
    no atómicamente. Este patrón es
    similar a la de los atributos de
    una propiedad declarada (ver "Propiedad
    Atributos de Declaración"). Tú has especificado la política para la relación usando una constante (ver
    objc_AssociationPolicy and
    Comportamientos Asociativos del Objeto).

Establecimiento de una asociación entre una matriz y una cadena

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

En el punto 1, la descripción de la cadena es todavía válido porque el OBJC_ASSOCIATION_RETAIN policy especifica que la matriz conserva la objeto asociado. Cuando la matriz es sin embargo, desasignada (en el punto 2)), visión general se libera y así en este caso también desasignado. Si tratas de, por ejemplo, registre el valor de descripción general, se genera un tiempo de ejecución salvedad.

 31
Author: visakh7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-19 14:47:54

objc_setAssociatedObject añade un almacén de valores clave a cada objeto Objective-C. Le permite almacenar estado adicional para el objeto, no reflejado en sus variables de instancia.

Es muy conveniente cuando desea almacenar cosas que pertenecen a un objeto fuera de la implementación principal. Uno de los principales casos de uso es en las categorías donde no se pueden agregar variables de instancia. Aquí usa objc_setAssociatedObject para adjuntar sus variables adicionales al objeto self.

Al usar la política de asociación correcta, su los objetos se liberarán cuando el objeto principal se desasigne.

 52
Author: Nikolai Ruhe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-06 09:44:13

Aquí hay una lista de casos de uso para asociaciones de objetos:

Uno: Para agregar variables de instancia a las categorías. En general, esta técnica esdesaconsejada , pero aquí hay unejemplo de un uso legítimo. Supongamos que desea simular variables de instancia adicionales para objetos que no puede modificar (estamos hablando de modificar el objeto en sí, es decir, sin subclase). Digamos que establecer un título en una 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

También, aquí es una bonita forma compleja (pero impresionante) de usar objetos asociados con categorías.. básicamente le permite pasar un bloque en lugar de un selector a un UIControl.


Dos: Añadiendo dinámicamente información de estado a un objeto no cubierto por sus variables de instancia junto con KVO.

La idea es que su objeto obtenga información de estado solo durante el tiempo de ejecución (es decir, dinámicamente). Así que la idea es que aunque se puede almacenar esta información de estado en una variable de instancia, el hecho de que está adjuntando esta información a un objeto instanciado en tiempo de ejecución y asociándolo dinámicamente con el otro objeto, está resaltando el hecho de que este es un estado dinámico del objeto.

Un excelente ejemplo que ilustra esto es esta biblioteca, en la que los objetos asociativos se utilizan con notificaciones KVO. Aquí hay un extracto del código (nota: esta notificación KVO no es necesaria para ejecutar el código en esa biblioteca.. más bien se pone allí por el autor por conveniencia, básicamente cualquier objeto que se registre en esto será notificado a través de KVO que los cambios le han sucedido):

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: echa un vistazo a esta discusión / explicación de objetos asociados por Mattt Thompson, autor de la biblioteca seminal AFNetworking

 24
Author: abbood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:43

Para responder a su pregunta revisada:

¿De qué sirve asociar el dispositivo con el controlador de vista si ya es una variable de instancia?

Hay varias razones por las que podría querer hacer esto.

  • la clase Device no tiene una variable o propiedad de instancia de controlador y no puede cambiarla o subclase, por ejemplo, no tiene el código fuente.
  • desea que se asocien dos controladores con el objeto device y no puede cambie la clase o subclase del dispositivo.

Personalmente, creo que es muy raro necesitar usar funciones de tiempo de ejecución de Objective-C de bajo nivel. Esto me parece un olor a código.

 5
Author: JeremyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-06 10:31:43