Ok I understand that this error mostly comes from sending a method call or trying to access a variable that has already been deallocated.
Here is the problem:
.h
#interface TimeEntry : NSObject <NSCopying, NSCoding> {
NSDate *from;
NSDate *to;
NSString *information;
}
#property (nonatomic, retain) NSDate *from;
#property (nonatomic, retain) NSDate *to;
#property (nonatomic, copy) NSString *information;
#end
And my classes' dealloc.
-(void)dealloc{
[super dealloc];
[to release];
[from release];
[information release];
}
This is the traceback thing when I get the EXC_BAD_ACCESS error
So I'm sending a message to an object that has been deallocated right?
So I turned on NSZombie and this STOPPED my crashes. It didn't give me some lovely crash report like I'd hoped. Instead it just kept the program from crashing.
In the dealloc method above if I comment out [to release] and [from release] the app doesnt crash. If I comment out just one of them.. it doesn't crash. In the debug window to and from have different memory addresses.
How can memory management be so hard!!!!
Any clues anyone?
Thanks,
Dan
Send the [super dealloc] message after you've released your variables, not before. [super dealloc] should be the last thing you do in your dealloc method.
Notice that you can send [nil release] and the application will not crash.
so checking for nil before releasing is some what unnecessary.
but i do agree that you should do [super dealloc] in the end of the dealloc procedure.
You should inspect all of the statements involving your properties to undestand where your variables are actually deallocated. Anyway, here is a quick solution that will avoid crashes, since your variables will be released if and only if they are still allocated objects:
-(void)dealloc{
if(to != nil{
NSLog(#"releasing: %#", to);
[to release];
}
if(from != nil{
NSLog(#"releasing: %#", from);
[from release];
}
if(information != nil){
NSLog(#"releasing: %#", information);
[information release];
}
[super dealloc];
}
Also, you may want to download, install and use the CLANG checker tool to understand why your code is wrong. This tool (which is already built for Leopard 10.5.x) may sometimes fail to provide the correct answer, but in my personal experience it never failed. I highly recommend it as one of your daily development tools.
You can download it from
http://clang.llvm.org/StaticAnalysis.html
Usage it really simple. Take a look at
http://clang.llvm.org/StaticAnalysisUsage.html#BasicUsage
In practice, you simply build your Xcode project using the command
scan-build -k -V xcodebuild
then, you inspect the resulting output HTML files using the command that will appears as output in your terminal window. These files will give you a detailed explanation of why something is wrong in your code.
Kind regards
Terry is correct, the [super dealloc] call must always be the last thing in -dealloc.
To be more specific, calling -[NSObject dealloc] is what deallocates the memory for the object. (In your case, you directly extend NSObject, but deeper inheritance trees create a chain of dealloc calls.) In a general sense, calling a parent's -dealloc method first will release resources inherited from the parent. If the child depends on any of those resources when it releases its own, you're up a creek. Since the parent cannot depend on the child, deallocing child resources first is the correct, safe way to do things.
The properties of the current object are stored in the object.
When you call [super dealloc], you are telling the system to destroy the object. After that call, you cannot rely on the properties being there or correct any more.
As previously stated, call it after you release the other members.
Related
The codes below I used on Xcode 3.2 and worked very well
#interface MoObject : UIViewController
{
NSMutableArray *categoryArray;
}
#property (nonatomic, retain) NSMutableArray *categoryArray;
#end;
#implementation MyObject
#synthesize categoryArray;
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *a = [[NSMutableArray alloc] init];
categoryArray = a;
[a release]; //AAA:I can not release this, it does not report compiling error , but after some operation, it will cause exec bad error
}
- (void)dealloc {
[category release];
[super dealloc];
}
I just move to Xcode 4.3.1 iOS 5.1
the same function causes so many exec bad error.
Even I close the ARC of whole project.
The problem still exist, it looks like I can not release the object array at point AAA
Welcome any comment
The point of ARC is that it handles the retain/release code for you. Since your array "a" is declared locally, when that function ends, ARC will insert the code release it for you. Same for dealloc - you don't need it with ARC (and actually can't use it).
But if you don't like or want to learn ARC you don't have to use it. It's an option when you create a new project.
You are not using ARC. If you were, code using -release would not even compile. Neither would calling [super dealloc] in your -dealloc method.
What you have is just an ordinary memory management bug. You are assigning a directly to the instance variable categoryArray. You are not using the setter which is synthesized for the property and it is the setter which does the proper memory management. Therefor, categoryArray ends up pointing to a deallocated object. If you were to use either self.categoryArray = a or [self setCategoryArray:a], which are equivalent statements, then your bug would go away.
Under ARC, this bug would be mostly hidden. The categoryArray instance variable would by default be __strong, so the array would be retained at least as long as categoryArray pointed to it and you wouldn't get EXC_BAD_ACCESS errors. However, it's still buggy to directly assign to it, bypassing the setter, except within initializer methods.
i have the following code in a .h
#property (nonatomic, copy) NSString *username;
Then, username is assigned in this way when the user enter text in a TextField:
self.username = textField.text;
And then, in dealloc method i call release:
NSLog(#"%d",[username retainCount]);
[username release];
NSLog(#"%d",[username retainCount]);
But in the console it prints:
2011-01-11 23:09:52.468 IApp[2527:307] 1
2011-01-11 23:09:52.480 IApp[2527:307] 1
What is the problem?
Thanks
After the release, the object is destroyed, so calling 'retaincount' a 2nd time has undefined behavior
What is the problem?
The problem is that you are using retainCount and expecting a meaningful result.
Do not call retainCount
When to use -retainCount?
The above has some good details. So does one of the answers here:
https://stackoverflow.com/questions/4580684/common-programming-mistakes-for-objective-c-developers-to-avoid
Note that you can set the MallocScribble environment variable and this will cause allocated memory to be filled with 0xaa bytes on allocation and 0x55 bytes on deallocation. In the face of that, your second call to retainCount would crash.
The objects are not released immediately. Probably there has to be a run loop cycle before the objects are really released.
However calling retainCount is really a mean thing. Please read why: When to use -retainCount?
EDIT: #kris-van-bael commented on this answer - correctly - that based on the documentation this is not true. So I have to clearly state that what I wrote here is based on testing this issue on iOS simulator - and it's not how it should things work. However it seems that the following code will run without an error:
#interface Test : NSObject { }
#property (retain, nonatomic) NSString *test;
#end
#implementation Test
#synthesize test;
#end
Then somewhere in your code write:
Test* t = [[Test alloc] init];
t.test = #"Test1";
NSLog(#"%#", t.test);
[t release];
t.test = #"Test2";
NSLog(#"%#", t.test);
This (unfortunatelly) will run with no error on iOS simulator (however executing it step-by-step with the debugger crashes), so there is clearly some trick on deallocating objects in iOS.
When you release the first time, the retain count is 1. Since the memory is about to be deallocated, there is no "need" to decrement the retain count, so the underlying code may simply skip the decrement step and deallocate the memory.
When you peek at the retain count the second time, you are looking at deallocated memory, which is unsafe and could potentially return any value. Since its immediately after the release, the memory probably hasn't been allocated to something else, but you shouldn't be accessing it regardless.
I am getting exception on few of my ViewControllers when I go through browsing the application.
The exception is occurring in viewdidunload, I think this is due to memory warning.
The following line gets an exception which are the IBOulet objects.
self.LabelDistance = nil;
self.distanceSlider = nil;
Please help.
Thanks
Why would you want to set this to nil?
If it's a #property (retain) UILabel * labelDistance; (and synthesized), then just release it in dealloc. Or do you fiddle with that ivar around?
One note: your variable and property should begin with a lower letter "l".
Try:
[self.labelDistance release];
[self.distanceSlider release];
instead. Also, you shouldn't be releasing ivars in viewDidUnload, release them in dealloc. If the problem persists, run the static analyzer (Build menu >> Build and Analyze), it is generally good at finding memory related issues.
Regarding over-releasing. Say I have a instance variable defined in Test.h
NSString *mystring;
In my implementation Test.m I do not initialize the variable mystring anywhere. But I release it in dealloc:
-(void)dealloc {
[mystring release];
}
Is this now over-released? I've been doing the following in dealloc to avoid any issues, however, is this really necessary?
-(void)dealloc {
if (mystring) [mystring release];
}
It seems that [nil release] shouldn't do anything, can someone verify this with class members?
There is no way to over-release something that never existed in the first place.
Instance variables are initialized to nil and, thus, [mystring release] is messaging nil which is just fine in Objective-C.
Your -dealloc methods do need to call [super dealloc] at the end, though.
First, why are you creating a variable at the class level that you're not initializing anywhere?
As this post is tagged iphone and it's IMPORTANT to manage your memory in the iphone environment it's always a good idea to release something if you've defined even if you don't assign it.
You can call release on an uninitialized variable without any problems, and be sure to [super dealloc]
Hey. I have been working on a Twitter application and have been stuck on a EXC_ BAD_ ACCESS error for quite some time. I know that EXC_ BAD_ ACCESS is a memory issue but i cannot pinpoint where the problem is. Here is my code sample:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = #"/Volumes/Schools/BHS/Student/740827/Documents/Forrest McIntyre CS193P/Presence2";
NSArray *propList = [NSArray arrayWithContentsOfFile:[NSBundle pathForResource:#"TwitterUsers" ofType:#"plist" inDirectory:path]];
people = [[NSMutableArray alloc]init];
for (NSString *name in propList) {
Person *p = [[Person alloc] initWithUserName: name];
[people addObject: p];
[p release];
}
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
The exception is thrown on the last brace after the comment. I believe that it is truly thrown in the for loop somewhere but just shows up upon exiting.
Here is the implementation file for Person:
#implementation Person
#synthesize image;
#synthesize username;
#synthesize displayName;
#synthesize statusArray;
-(id)initWithUserName:(NSString *)userName {
if(self = [super init])
{
self.username = userName;
NSDictionary *info = [TwitterHelper fetchInfoForUsername:userName];
self.displayName = [info objectForKey:#"name"];
NSLog([NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]]);
NSString *imageURL2 = [NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]];
self.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL2]]];
[info release];
self.statusArray = [TwitterHelper fetchTimelineForUsername:userName];
}
return self;
}
#end
Thanks for any help
EDIT: Here is the header file for PersonListViewController (the class that contains the ViewDidLoad).
This is just to show you where people is coming from.
#interface PersonListViewController : UITableViewController {
NSMutableArray *people;
}
#end
since you never retain propList or path you shouldn't be releasing them.
You should, however, release people
For an overview of memory management, see the Memory Management Programming Guide
For quick fixes, try the static analyzer.
I think the problem is here:
[propList release];
Since you created propList using arrayWithContentsOfFile you don't need to release it - it will be automatically released. The autorelease is actually what's causing the error since it is trying to release something that you already released manually.
ETA: as cobbal mentioned, you also don't need to release path.
Debugging EXC_BAD_ACCESS is difficult to debug. This happens when a message is sent to an object that is already released. You need to find out what is causing this generic error by turning on NSZombiEnabled environment variable so the Objective-C environment will be able to 'track' a deallocated object. Using this, when you get the error you can determine where the error occurred by looking at the call stack. You won't know where it is released but at least it will get you close.
I don't have it setup here, but you may also be passing a pointer to the error which will cause the object to not persist as a zombie/dummy.
Bottom line, you need to make sure the variables you are meaning to release, that you retain them as necessary.
This Technical Q&A by Apple gives tips on Finding bugs with EXC_BAD_ACCESS.
For one, neither of these are necessary in your example:
[path release];
[propList release];
because:
path is a string literal (will always exist)
propList is autoreleased
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
http://www.cocoadev.com/index.pl?NSZombieEnabled can be useful in tracking down EXC_BAD_ACCESS bugs. Instead of deallocating objects when they are released it puts them into a zombie state that raises an exception when they are subsequently accessed. Just be sure not to ever release code with this flag set, as it will leak memory like a sieve.
what is self.editButtonItem? I don't see it in your .h file
A couple of things.
In initWithUserName: you're getting info from a method that doesn't contain alloc/copy/create. Further, you don't explicitly retain it. Yet you release it. This is problematic assuming fetchInfoForUsername: autoreleases its result as expected according to the Cocoa Memory management rules.
Using property accessors in initializers is considered bad form since it can cause KVO notifications to be sent out for a half-baked instance.