Using objc_setAssociatedObject with weak references

One more option similar to WeakObjectContainer:

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

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

After trying it out, the answer is NO.

I ran the following code under the iOS 6 Simulator, but it would probably have the same behavior with previous iterations of the runtime:

NSObject *test1 = [NSObject new];

NSObject __weak *test2 = test1;

objc_setAssociatedObject(self, "test", test1, OBJC_ASSOCIATION_ASSIGN);

test1 = nil;

id test3 = objc_getAssociatedObject(self, "test");

In the end, test1 and test2 are nil, and test3 is the pointer previously stored into test1. Using test3 would result in trying to access an object that had already been dealloced.


As ultramiraculous demonstrated, OBJC_ASSOCIATION_ASSIGN does not do zeroing weak reference and you risk to access a deallocated object. But it’s quite easy to implement yourself. You just need a simple class to wrap an object with a weak reference:

@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

Then you must associate the WeakObjectContainer as OBJC_ASSOCIATION_RETAIN(_NONATOMIC):

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

and use the object property to access it in order to get a zeroing weak reference:

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