objective-c weak-references objective-c-runtime

objective c - Usando objc_setAssociatedObject con referencias débiles



objective-c weak-references (4)

Después de probarlo, la respuesta es NO.

Ejecuté el siguiente código en el simulador de iOS 6, pero probablemente tendría el mismo comportamiento con las iteraciones anteriores del tiempo de ejecución:

NSObject *test1 = [NSObject new]; NSObject __weak *test2 = test1; objc_setAssociatedObject(self, "test", test1, OBJC_ASSOCIATION_ASSIGN); test1 = nil; id test3 = objc_getAssociatedObject(self, "test");

Al final, test1 y test2 son nulos, y test3 es el puntero previamente almacenado en test1. El uso de test3 resultaría en intentar acceder a un objeto que ya había sido desasignado.

Sé que OBJC_ASSOCIATION_ASSIGN existe, pero ¿es cero la referencia si el objeto de destino se desasigna? ¿O es como en los viejos tiempos en los que esa referencia debe ser nula o nos arriesgamos a un acceso incorrecto más adelante?


Este comportamiento no se especifica en los documentos o encabezados como mejor puedo decir, por lo que es probable que sea un detalle de implementación con el que no debería contar, incluso si fue capaz de discernir cuál es el comportamiento actual. Supongo que no se pone a cero. Este es el por qué:

En general, no es necesario anular las referencias en iVars durante -dealloc . Si un objeto se desasigna, no debería importar si sus iVars se pusieron a cero, porque cualquier otro acceso adicional al objeto desasignado o sus iVars es, en sí mismo, un error de programación. De hecho, he escuchado a algunos argumentar que es mejor no borrar las referencias durante -dealloc , porque hará que los accesos erróneos sean más obvios / expondrán los errores antes.

EDIT: Oh, supongo que he leído mal su pregunta. Quieres "poner a cero las referencias débiles". El almacenamiento asociado no parece admitirlos. Podría hacer una clase de paso trivial con una propiedad / ivar marcada como __weak y lograr el mismo efecto de esa manera. Un poco de kludgey, pero funcionaría.


Una opción más similar a WeakObjectContainer :

- (id)weakObject { id (^block)() = objc_getAssociatedObject(self, @selector(weakObject)); return (block ? block() : nil); } - (void)setWeakObject:(id)object { id __weak weakObject = object; id (^block)() = ^{ return weakObject; }; objc_setAssociatedObject(self, @selector(weakObject), block, OBJC_ASSOCIATION_COPY); }


Como demostró ultramiraculous, OBJC_ASSOCIATION_ASSIGN no hace cero la referencia débil y usted corre el riesgo de acceder a un objeto desasignado. Pero es muy fácil implementarse. Solo necesitas una clase simple para envolver un objeto con una referencia débil:

@interface WeakObjectContainer : NSObject @property (nonatomic, readonly, weak) id object; @end @implementation WeakObjectContainer - (instancetype) initWithObject:(id)object { if (!(self = [super init])) return nil; _object = object; return self; } @end

Entonces debes asociar el WeakObjectContainer como OBJC_ASSOCIATION_RETAIN (_NONATOMIC):

objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainer alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

y use la propiedad de object para acceder a ella y obtener una referencia débil de reducción a cero:

id object = [objc_getAssociatedObject(self, &MyKey) object];