views:

181

answers:

2

I have two classes, ClassA that will instantiate ClassB and pass off a method as a delegate. ClassB will eventually invoke ClassA's delegate. Do I need to add a retain on ClassA when ClassB stores it?

I'm following the "Implementing a Delegate for a Custom Class" from the "Cocoa Fundamentals Guide: Communicating with Objects" but the sample code demonstrated doesn't seem to take memory management into account.

ClassA will set the delegate and expect to be called back later when ClassB is done with it's work.

@implementation ClassA

-(void)launchSomething
{
   ClassB *classB = [[ClassB alloc] init];
   [classB setCallback:self withSelector:@selector(deferredWork)];

   // do some other stuff, assign class B to some View and eventually release class B
}

-(void)deferredWork
{
   NSLog(@"this is the method that will be deferred till some point in time");
}

Header file for the ClassB that'll store and then later invoke the delegate:

@interface ClassB

id targetObject;
SEL targetMethod;

-(void) setCallback:(id)anObject withSelector:(SEL)aMethod

ClassB's implementation:

@implementation ClassB
-(void) setCallback:(id)anObject withSelector:(SEL)aMethod
{
   // QUESTION: Do I need to add a 'retain' here on the targetObject?
   targetObject = anObject;
   targetMethod = aMethod;
}

-(void) someWorkLater
{
    if ( [targetObject respondsToSelector:@selector(targetMethod)] ) {
        // invoke the target object with the specific method
        [targetObject targetMethod];
    }
}
+2  A: 

You wouldn't retain ClassA in ClassB, because ClassA already owns ClassB and it's assumed that when ClassA is deallocated, it will take care of cleaning up any references in ClassB.

If you followed the "normal" rules of ownership and retained ClassA when setting the delegate method in ClassB, you'd end up with a retain loop where neither objects would ever be deallocated. Instead, you should be using a weak reference exactly like you are.

Marc Charbonneau
A: 

As Marc said, the normal practice in Cocoa is for delegates to be "weak" links. That is, they are not retained. It is up to the delegate to ensure that when it is no longer able to respond as a delagate nothing bad happens - either by setting the delegate to nil or releasing the source object (assuming it is the only owner and it will be immediately release).

So in your example, if classB remains around after the end of launchSomething, then you presumably have stored it in an ivar. Your dealloc routine for classA would either have

[classB setCallback:nil]; // optionally withSelector:@selector(none)

and/or

[classB release];

If classB might have any other owners then you should definitely use the setCallback:nil, but often you know you are the only owner.

Things get complicated when there are views and windows involved as it can be hard to ensure the order objects are released and that no one else has a strong link to classB, in which case clearing the callback is essential.

The same applies to observers and notifications which are typically weak links and so much be cleared in your dealloc routine.

Peter N Lewis