I’m having trouble with a broken pointer that’s pointing to garbage after an object has been released. objectA is the delegate for a callback from another object, objectB.
objectA is being allocated and released quite often (in my application its a menu UI object). Every time objectA is being initialised it initialises objectB and begins an asynchronous operation and then calls back to objectA via the id delegate property.
How can I stop my pointer: id delegate from breaking ?
ObjA.m
-(void)dealloc{
[super dealloc];
}
+(id)init{
ObjA *objectA = [[ObjA alloc]init];
return objectA;
}
-(id)init{
if (self == [super init]){
ObjB *objectB = [ObjB initialiseWithDelegate:self];
}
return self;
}
-(void)callback:(id)arg{
//This callback is called from an asynchronous routine from objectB
}
-(void)terminate{
//This is called when I want to remove/release objectA
//Other logical termination code here
[self release];
}
ObjB.m
@synthesize delegate;
+(id)initialiseWithDelegate:(id)delegate{
ObjB *objectB = [[ObjB alloc]init];
[objectB setDelegate:delegate];
return objectB;
}
-(id)init{
if (self == [super init]){
[self beginAsynchronousOperation];
}
return self;
}
-(void)beginAsynchronousOperation{
//Do asynchronous stuff here
}
-(void)finishedAsynchronousOperation{
//Called when asynch operation is complete
if (self.delegate) [self.delegate callback:someargument]; //EXC error. Pointer broke
}
The short answer here is that you
nilout objectB’sdelegateproperty when you dealloc objectA. Because delegates are assigned, and not retained (explicitly to prevent retain cycles), as you have seen, the delegate reference can be left hanging when the “owning” object goes away. Typically objectA will be holding a retained reference to objectB, and during objectA’sdealloc, it will first set objectB’sdelegatetonil, and thenreleaseobjectB. This will prevent the crash. Of course this assumes (as is typical) that you don’t need to do anything with that async completion. It also assumes that objectB can safely be released by objectA when it is in the middle of an async operation. This is usually true of (say) animations, but if you’re building your own tasks, you might need to be careful of the lifetimes here.Some notes on this code snippet that might be helpful:
Your objectA isn’t actually holding a reference to objectB once it’s created. This means you can’t nil out the delegate. You should keep the reference to objectB when it’s created so you can do this.
You are leaking objectB. ObjectA creates it (alloc, init), but then drops the reference, so even when it’s done, no one seems to be responsible for releasing it. Again, holding it so you can release it will fix this too.
Your
-terminateon objectA is an anti-pattern– an object should never (with only one exception: a failure insideinit) be calling-releaseonself. An object should be released by its owner, which is whoever originally created it.Your pattern of
if (self == [super init])is normally written with one equals sign, meaning that you’re both assigningselfto the result as well as checking it fornil. This is a Cocoa historical oddity, and probably makes no difference here, but worth pointing out.