Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create weak reference in Objective-C?

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

like image 362
Matrix Avatar asked Sep 01 '10 13:09

Matrix


3 Answers

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

like image 90
Nick Forge Avatar answered Sep 29 '22 12:09

Nick Forge


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.

like image 26
cobbal Avatar answered Sep 29 '22 12:09

cobbal


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.

like image 41
Georg Fritzsche Avatar answered Sep 29 '22 14:09

Georg Fritzsche