I have a weird issue that comes up while releasing an object. In the object's dealloc method I am releasing another (sub) object. When this sub object is released I get an EXC_BAD_ACCESS error. I am pretty sure the sub-object is a valid pointer right before I call release on it.
This is how I've confirmed the weirdness - I set a break point inside the dealloc method, right before the release of the sub-object, and it is allocated! I can send it messages and it responds correctly. Another weird bug is that, if NSZombieEnabled is set to YES, I dont get an error at all, not even NSZombie's usual error that says I've sent a message to a deallocated object, the code runs properly.
Does anybody have any idea how to solve this?
Thanks in advance!
* EDIT 1 *
I've found out that if I place the [super dealloc] in the end of the dealloc, it crashes, but if I place it at start, it won't. I've had something similar before, only the other way - app crashed if I [super dealloc] in start of my dealloc, and didn't at the end.
Why does it make a difference and when should you use what?
* EDIT 2 *
Scratch Edit #1! It doesn't work either way.
Do your dealloc routine happen to look like this:
- (void)dealloc
{
[super dealloc];
[otherObject release];
}
if so, change the order. If you call [super dealloc], your object gets vanished from memory, trying to acces otherObject later will cause you to access non-allocated memory -> EXC_BAD_ACCESS.
Without seeing how the object is initialized, will be hard for anyone to know for sure.
MyObject *someObject = [MyObject alloc] init...]; // Make sure you aren't autoreleasing here
// do some stuff with someObject
[someObject release];
Related
I thought I understood memory management well enough until this issue happened (Mac OS X 10.6): I have a custom NSView subclass with an NSMutableArray instance variable, but when I dealloc my view and attempt to release that instance variable, sometimes BOOM, EXC_BAD_ACCESS happens. This happens when I try to close my document window without quitting the program, but for some reason, even under identical conditions, sometimes it works without issue. Can anyone help me understand what's going on here? The relevant bits of code from my NSView subclass:
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
rainbow = [[NSMutableArray alloc] initWithObjects:
// some objects go here, followed by the nil sentinel
]
return self;
}
return nil;
}
And the dealloc method:
- (void)dealloc {
[super dealloc];
NSLog(#"Release the rainbow!");
if (rainbow) {
[rainbow removeAllObjects]; // EXC_BAD_ACCESS happens here
[rainbow release];
}
}
Even though I check whether rainbow is still around, sending it a message results in that segfault. There is one spot where it gets used: it's passed as a *info argument to a CGShading callback function. Here are the relevant bits of that function (which generally works without crashing):
NSMutableArray *colorStops = (NSMutableArray *)info;
[colorStops retain];
/*
...
*/
[colorStops release];
I'm guessing that there's something here about threads, but I really don't know. Anyone have any ideas? Thank you very much! I've reread the memory management guide; any more headdesking on this and my glass tabletop shatters in my face.
Always do
[super dealloc]
at the end of your dealloc method.
In addition to Terry's point about [super dealloc], the -removeAllObjects call will message all of the objects in the array (to release them). If you have overreleased any of those objects, the pointer that the array has may now point to deallocated or otherwise invalid space.
So, you have to review your memory management of all of the objects in the array. Run your app under the Zombies instrument. Do a Build and Analyze and resolve the identified issues.
There have been a lot of discussions on why you should not use setter in dealloc like you do in viewDidUnload. Question is, why not just use [property release] in both dealloc or viewDidUnload? Doesn't that end the discussion or is there a reason why one should do self.property = nil in viewDidUnload instead?
That is, we always follow this idiom for everything - rather than using one form in viewDidUnload and another in dealloc, which seems senseless.
[ivar release];
ivar = nil;
The general idea is that nothing will happen with an object after dealloc, so you can just release property and don't worry about it being invalid pointer. On the other hand, if you do [property release] in viewDidUnload and not set it to nil, it will contain invalid pointer and this may lead to crash if property is used before new valid value assigned to it.
Properties are save for this, you can set nil them everywhere, because them save releasing (setters are looking like -(void)setProp { [prop release]; self.prop = nil; }).
But if you use [something release], you should do it in viewDidUnload method, because view can be unloaded, but controller not deallocated, which means possible memory leak in viewDidLoad. And if you call [something release] twice, it will cause crash for wrong counter decrease
All the other responses consider the two ways the same. But consider when you have an atomic property. It's a lot shorter and cleaner than the "pure" way.
This code down below works as expected, cleans things up without Zombies. The class in which this method exists, is the owner of the Nodes, which are being released, yet, upon "Analyze" the following 2 issues show up.
If possible, could you help me understand why?
- (void) dealloc {
NSLog(#"Releasing [Doubly Linked List] .. ");
Node *thisNode = [self firstNode];
Node *nextNode = [thisNode next];
while (nextNode != nil) {
// If "Next node" is not nil, it means that
// "previous node" can now be released
NSLog(#" - releasing node \"%c\"", [[nextNode previous] charData]);
[[nextNode previous] release];
nextNode = [nextNode next];
}
[[self lastNode] release];
[super dealloc];
}
Click on the icon on the left of the message, it will show the path through the code that produces the error.
You are releasing something, [nextNode previous] that you do not own. In particular you did not alloc or retain it nor obtain it from a method that begins with new or copy so you do not have ownership of it and should not release it.
It is also very uncommon to release something not in your class, [[nextNode previous] release].
Now: [[self lastNode] release];
As above you did not obtain an ownership on the object you are releasing.
If lastNode is a property with retain you are subverting the setter and when later a value is assigned to lastNode via the setter there will be an extra release on the object and probably a crash. If it is not a property again this it very non-standard to release something that is returned by a method call.
Any releases with code of this form [[self lastNode] release] is non-standard and if be avoided there will be fewer ownership (retain/release) problems.
You will save a lot of time and grief by studying the Apple memory management documentation.
The issue is that you are not doing things according to the ordinary memory management conventions of Objective C and the static analyzer is getting confused. Basically, the Objective C class which allocates an object "owns" the object and is responsible for releasing it. Here, you are not using those conventions, so the analyzer is complaining, even if what you are doing works correctly.
See Apple's documentation for more on ownership.
Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.
I have an array of 5 items that is also used as the content for a tableview. In the nib is a button that changes the content of the array to 5 different items. When I click that button, however, the app crashes with an EXC_BAD_ACCESS. I set a breakpoint on objc_exception _throw and had my suspicions confirmed. The bad method is
- (id)tableView:(NSTableView *)wordsTableView
objectValueForTableColumn:(NSTableColumn *)column
row:(int)rowIndex
{
return [[currentCard words] objectAtIndex:rowIndex];
}
currentCard is an instance of the GameCard class, and its array, words, is the array in question. On first launch, it works fine, but if I try to change it, crash.
----------EDIT----------
In AppController's awakeFromNib: I have this
currentCard = [[GameCard alloc] init];
And in the button's IBAction, I have this:
[currentCard release];
currentCard = [[GameCard alloc] init];
With zombies enabled, when I click the button, I get this from GDB:
2009-06-22 18:55:03.368 25WordsMax[19761:813] *** -[CFArray objectAtIndex:]: message sent to deallocated instance 0x14ba00
referring to the data source method. I've been trying to track down the memory bug for hours, but am missing it.
I got so frustrated I commented out every retain & release (no autoreleases) in the code and still get 2009-06-22 19:41:58.564 25WordsMax[21765:813] *** -[CFArray objectAtIndex:]: message sent to deallocated instance 0x14c330 when I hit the button.
And what is calling the datasource method? I'm not calling reloadData. If in my datasource method, I return #"A Word" for each row, everything runs fine. In GDB, I can even see my NSLogs printing the contents of the new array, all without a hitch. It's only when the datasource method as seen in the question gets called that any problems happen.
2009-06-22 18:55:03.368 25WordsMax[19761:813] *** -[CFArray objectAtIndex:]: message sent to deallocated instance 0x14ba00
Read that carefully. The receiver was an instance of CFArray, and the message selector was objectAtIndex:.
The deallocated instance was an array, not a game card.
Your game card owns the array, so it must retain it in some way. This means you must either:
Create the array using alloc and init (and not autorelease it).
Retain it.
Make its own copy. (Preferable if you're receiving an array from another object, as in your setter, since the array you receive may be mutable, and you don't want the other object mutating “your” array.)
If you synthesize your accessors, declare the property as either #property(retain) or #property(copy). Note that this won't work if you want a mutable copy; in that case, you'll have to implement the setter yourself.
Sounds like you have a reference counting error somewhere, which isn't going to be debuggable from just that snippet of code. Try running with NSZombieEnabled=1, and then looking for retain/release bugs where you manage whatever object you get zombie warnings about.
NSZombie is telling you that the object returned from [currentCard words] is getting released somewhere, then you're using it again. Check all of your assignments to that variable and make sure that you're not setting it to an autoreleased object. Then check all release and autorelease messages sent to it.
It may help to see the GameCard Init and the GameCard dealloc method.
Also to verify that the method you think is the problem is the problem I would check for nil
if( [currentCard words] != nil )
return [[currentCard words] objectAtIndex:rowIndex];
else
return #"Blah!";