How to create weak reference in Objective-C?

10,144

Solution 1

I would question the reason you need to do this in the first place - but if you do decide it's the best data architecture, I would use NSValue. To store a reference to an object (without retaining it) in a Foundation collection class, you can use +[NSValue valueWithNonretainedObject:]:

NSMutableArray *a = [[NSMutableArray alloc] initwithObjects:@"one", nil];
NSMutableArray *b = [[NSMutableArray alloc] initwithObjects:@"two", nil];

[a addObject:b];
[b addObject:[NSValue valueWithNonretainedObject:a]];

Then to get your object later on from the NSValue instance, you would do this:

NSMutableArray *c = (NSMutableArray *) [[b objectAtIndex:index] nonretainedObjectValue];

Addendum

I've changed the answer from using +[NSValue valueWithPointer:] to +[NSValue valueWithNonretainedObject:], since under ARC, the compiler will complain (due to casting from an object type to const void *). While functionally the same, the typecasting and method name makes more sense (and the compiler will actually let you compile without resorting to hackery).

Solution 2

The way I've solved this problem in the past is to use a subclass of NSProxy to break the cycle. I'll have an object that stores a weak reference to one of the arrays and passes all messages except memory management ones through to it.

     ┌──── NSArray A <────┐
     │                    │
     │                    │
     v        weak        │
ACWeakProxy ┈ ┈ ┈ ┈ ┈ > NSArray B

@interface ACWeakProxy : NSProxy {
    id _object;
}

@property(assign) id object;

- (id)initWithObject:(id)object;

@end

@implementation ACWeakProxy

@synthesize object = _object;

- (id)initWithObject:(id)object {
    // no init method in superclass
    _object = object;
    return self;
}

- (BOOL)isKindOfClass:(Class)aClass {
    return [super isKindOfClass:aClass] || [_object isKindOfClass:aClass];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation setTarget:_object];
    [invocation invoke];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [_object methodSignatureForSelector:sel];
}

@end

Then your code becomes

NSMutableArray * A = [[NSMutableArray alloc] initwithObjects:@"one", nil];
NSMutableArray * B = [[NSMutableArray alloc] initwithObjects:@"two", nil];

[A addObject:B];
ACWeakProxy * proxy = [[ACWeakProxy alloc] initWithObject:A];
[B addObject:proxy];
[proxy release];

// will print "two"
NSLog(@"%@", [[[B objectAtIndex:1] objectAtIndex:1] objectAtIndex:0]);

It is, however, up to you to make sure your weak reference doesn't vanish while you are still using it.

Solution 3

You could:

  • manually break the cycle by removing the objects
  • use CFArrayCreateMutable() and set the retain and release callback to NULL:

    CFArrayCallBacks acb = { 0, NULL, NULL, CFCopyDescription, CFEqual };
    NSMutableArray *noRetain = (NSMutableArray *)CFArrayCreateMutable(NULL, 0, &acb);
    

Even better though, take a step back and think about how you can achieve what you want differently. With a proper design this problem might not even occur.

Share:
10,144
Matrix
Author by

Matrix

I am working as an iphone application developer.

Updated on June 09, 2022

Comments

  • Matrix
    Matrix about 2 years

    i have a situation like this:

    NSMutableArray * A = [[NSMutableArray alloc]initwithObjects:@"one",nil];
    NSMutableArray * B = [[NSMutableArray alloc]initwithObjects:@"two",nil];
    
    [A addObject:B];
    [B addObject:A];
    

    now here is a retain cycle, How can i break this retain cycle? (using weak reference)....in above example. Thanks