views:

178

answers:

6

Hi, 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

A: 

In iPhone, there is no concept of weak reference. In your cases, I believe that it will cause the problem of over releasing on either of them.

Usually, for 2 classes, I will do something like this:

Let one class own another by retaining and the other just use assign. Some sample code may clarify it

class A

@interface A {

}

@property (nonatomic, retain) B *b;
@end

class B

@interface B {

}
@property (nonatomic, assign) A *a;
@end
vodkhang
+3  A: 

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.

Georg Fritzsche
A: 

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.

cobbal
A: 

In the iPhone environment without garbage collection, a weak reference is more or less defined as a reference where the object is not retained. With that in mind (actually even without that in mind), you have no control over whether NSMutableArray creates a weak reference or not. In the GC environment, you still have the cycle, but it doesn't matter because once both objects unreachable, they'll both go away.

The only thing you can do is to manually break the cycle. You do that by removing one of the arrays from the other.

JeremyP
A: 

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 valueWithPointer:]:

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

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

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

NSMutableArray *c = (NSMutableArray *) [[b objectAtIndex:index] pointerValue];
Nick Forge