How to create weak reference in Objective-C?
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 toNULL
: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.
Comments
-
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