简体   繁体   中英

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

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

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:

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

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM