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
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];
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).
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.
You could:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With