exc bad access iphone error - iphone

I am getting an error called Thread 1: Program recieved signal:"EXC_BAD_ACCESS"
here is my code
[gameArray removeLastObject];
[gameArray addObject:shotArray];
[gamesArray removeObjectAtIndex:gameNumber];
[gamesArray insertObject:gameArray atIndex:gameNumber];
NSString *path = [self findGamesPath];
[NSKeyedArchiver archiveRootObject:gamesArray toFile:path]; // the error is here
Why is there an error? Is something being released too many times?
Here is the findGamesPath code
-(NSString *)findGamesPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
NSString *result = [documentFolder stringByAppendingPathComponent:#"iShotTrackGames.plist"];
return result; }

More likely than not, something is being released too many times. Turn on zombie detection and try again.
Before you do that, though, try performing a "Build and Analyze" on your code and fix any problems it identifies.
I see this is tagged with "thread-safety". Why? I.e. what are other threads potentially doing while the above code is running?

EXC_BAD_ACCESS means you've got a bad pointer somewhere. These errors are often pretty easy to spot because the bad pointer is one of the parameters to the method called on the line where the error occurs. In this case, though, the archiver is going to walk the entire object graph pointed to by gamesArray, and the bad pointer could really be anywhere in there. Follow #bbum's advice to turn on NSZombies -- that'll help you figure out which pointer is the problem.

I think I know what's happening. Your path string is losing scope inside the findGamePath method.
Are you allocating the path string inside that method and returning it?
Once it loses scope it's released from memory.
Trying to access that string then will cause EXC_BAD_ACCESS as you're accessing a released object.
Could you post the findGamePath code here? That may clarify the issue more.
Oh and this has more to do with memory management than thread safety.

Related

Objective-C release is not called explicitly

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * str = [[NSString alloc] initWithString:#"test"];
[str release];
int i = 999999999;
while(i-- > 0) {}
NSLog(#"%#", str);
[pool drain];
Output: test
Why didn't release work?
How can I immediately delete the object from memory?
Xcode Version 4.0 iPhone Application
~SOLVED~
Thank's to all for answers.
I've got a lot of useful information about this question. I'm going to use NSString *str = #"text" instead of NSString *str = [[NSString alloc] initWithString:#"text"];
i've understood that release just "marks" memory as "willing to be freed", but not freeing it immediatly
It did work. You have relinquished ownership of that object, and when the system determines that it is no longer owned, it will be marked available for reuse by the system. That may happen immediately, if you were the only owner of the string. It may happen at some later point, if creation of the string caused it to be autoreleased internally. Or, as Dave DeLong points out, the system may optimize it into an object that is never released.
In your case, it's being optimized into a constant string, which will exist for the life of the program. If you were to use an NSMutableString instead of an NSString, you'd see funky behavior that would probably not crash, but wouldn't print what you expected. (See this question for an example.)
If you used an NSArray instead, it would be deallocated when you called release, but you'd still see your NSLog example work correctly until you allocated some other object. Deallocation just marks the memory as available for reuse; it doesn't actually clear it out. So if you passed the array to NSLog, that memory hasn't been changed and thus it still prints correctly.
The key point in all of this, though, is to recognize that calling release will not necessarily cause the object to be deallocated. It may continue to exist for any number of reasons. But once you call release, you have relinquished ownership of the object. If you continue using it after that point, the system is free to do all sorts of weird things at its own will, as demonstrated.
Release does work but what you are attempting to do has undefined behavior, and when using a NSString and a literal you may also get different behavior. What is happening is although your object is released the memory at that location is reclaimable and has not changed and when it goes to print it it is still valid. Since it is a NSString a message to description is not necessarily sent and that is why you are not getting an exception for attempting to message a deallocated object.
This question has some good information about NSString and NSLog.
When you do:
NSString * str = [[NSString alloc] initWithString:#"test"];
This gets optimized into:
NSString * str = #"test";
You can't release a constant string, because it's hardcoded into the application binary.
Proof:
NSString *s = [NSString alloc];
NSLog(#"%p", s);
s = [s initWithString:#"foo"];
NSLog(#"%p", s);
s = #"foo";
NSLog(#"%p", s);
Logs:
2011-04-12 10:17:45.591 EmptyFoundation[6679:a0f] 0x100116370
2011-04-12 10:17:45.599 EmptyFoundation[6679:a0f] 0x100009270
2011-04-12 10:17:45.604 EmptyFoundation[6679:a0f] 0x100009270
You can see that the result of +alloc is different from the result of -initWithString:, and the result of -initWithString: is equivalent to the constant string. Basically, -initWithString: says "aha, i'm going to be an immutable string, and I'm being given an immutable string! I can just take a shortcut, destroy myself, and return the parameter, and everything will still work the same"
You're using a bad pointer in you NSLog(). You happen to be getting lucky in this case, but you should expect code like this to crash or fail in other ways.
There is no need to delete the memory block, this will use up an unneeded cycle.
The memory will be overridden when an new object is allocated an occupy that memory block.

Nasty NSPropertyListSerialization Leak

NSString *anError = nil;
id plist;
plist = [NSPropertyListSerialization propertyListFromData:rawCourseArray mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&anError];
if (anError != nil){
[anError release];
}
The above code causes a memory leak every time I call it. I am releasing the error but still there is a leak. I haven't seen any resolution to this issue. I posted this already and most respond that this is not a leak. But see here in the leak performance tool:
I need this fixed because eventually my app crashes. Any ideas? Many thanks
I had the same problem. Used propertyListWithData:options:format:error: with same result. Used NSDictionary initWithContentsOfFile with same result:
Leaked Object # Address Size Responsible Library Responsible Frame
NSCFNumber,19 < multiple > 304 Bytes MediaToolbox FigRemote_CreatePropertyListFromBinaryPListData
FWIW - I only have dictionaries and arrays and strings in the pList, no numbers.
The issue is not the error object (or string in your method). The issue is not plist above, since it should be autorelease. In the end, I gave up and reimplemented using JSON and:
http://stig.github.com/json-framework/api/index.html
==> no more leak.
Best,
Fred
I've had the same problem before.
Basically this is because your error variable got released without initiating it. So what you can do is try to init your NSString *error to an empty string before releasing it. Or not releasing it at all.

Why does this crash: stringByAppendingFormat

My code crashes at this function (at the stringByAppendingFormat: with error objc_msgSend() selector name: stringByAppendingFormat).
This is that line:
// imagesPath = ...iPhone Simulator/4.0/Applications/NUMBERS/Documents/images
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath stringByAppendingFormat:#"/%d.png", [[self.postsArrayID objectAtIndex:row] intValue]]];
Could it have something to do with the retaining of objects?
Thanks :)
> rootPath =
> [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
> NSUserDomainMask, YES)
> objectAtIndex:0]; imagesPath =
> [rootPath
> stringByAppendingPathComponent:#"/images/"];
Hah! Setting a property and setting
value using self.imagesPath =... fixed
it. Obj-c is so hard to understand
sometimes...
The methods you used to set the get the paths are autoreleased, so when you tried to access them later they had already died. Using the self.imagesPath property will retain the data (you specified it as (nonatomic, retain) - so it will stay around until you release it (or assign anything else using the property accessor self.imagesPath = ....;
Apple's memory management guide is highly recommended, though it is still easy to fail after reading it a couple of times. :-)
Usually a crash in objc_msgSend() implies the message being passed to the object (in this case, stringByAppendingFormat) is not specified for that object. Quick googling reveals that many top pages for stringByAppendingFormat are quite dated, inferring the API has possibly been deprecated in favor of something else.
As a workaround, it would seem +[NSString stringWithFormat:] would be a viable alternative for your use case.
Why not use stringByAppendingPathComponent:? And surely imagesPath is not ........../NUMBERS/images? Would it not be ................/<random ID>/images?

Why am i getting a EXC_BAD_ACCES

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.

How do i resolve EXC_BAD_ACCESS errors encountered in iphone development

I'm trying to do a simple thing; read an image from the internet, save it to the app's documents directory on the iphone, and read it back from that file so that i can do other things with it later. Writing the file works fine but when i try to read it back i get an EXC_BAD_ACCESS error in GDB that i have no idea how to resolve. Here is what my code basically looks like:
-(UIImage *) downloadImageToFile {
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release]
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[data writeToFile:path atomically:YES];
return [[UIImage alloc] initWithContentsOfFile:path];
}
The code fails in the return statement when i try to initialize the UIImage from the file. Any ideas?
Edit: neglected to add a release that was the problem in the code originally.
Note: This applies specifically to non-ARC memory management.
Since this has had so many views and the checked answer appropriately states that "code shows a severe lack of knowledge of how memory management works in Objective-C," yet no one has pointed the specific errors out, I figure I'd add an answer that touched on them.
The baseline-level rule that we have to remember about calling methods:
If the method call includes the words alloc, new, copy, or retain, we have ownership of the object created.¹ If we have ownership of an object, it's our responsibility to release it.
If the method call does not contain those words, we do not have ownership of the object created.¹ If we don't have ownership of an object, releasing it is not our responsibility, and we therefore should never do it.
Let's look at each line the OP's code:
-(UIImage *) downloadImageToFile {
We started a new method. In doing so we have started a new context in which each of the created objects lives. Keep this in mind for a bit. The next line:
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
We own url: the word alloc there tells us that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
We do not own paths: no use of the four magic words, so we do not have ownership and must never release it ourselves.
NSString *documentsDirectory = [paths objectAtIndex:0];
We do not own documentsDirectory: no magic words = no ownership.
[paths release]
Going back a couple of lines we see that we don't own paths, so this release will cause an EXC_BAD_ACCESS crash as we try to access something that no longer exists.
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
We do not own path: no magic words = no ownership.
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
We own data: the word alloc there tells use that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
The following two lines don't create or release anything. Then comes the last line:
}
The method is over, so the context for the variables has ended. Looking at the code we can see that we owned both url and data, but didn't release either of them. As a result our code will leak memory every time this method is called.
The NSURL object url isn't very big, so it's possible that we might never notice the leak, though it should still be cleaned up, there's no reason to leak it.
The NSData object data is a png image, and could be very large; we are leaking the entire size of the object every time this method is called. Imagine this was called every time a table cell was drawn: it wouldn't take long at all to crash the whole app.
So what do we need to do to fix the problems? It's pretty simple, we simply need to release the objects as soon as we no longer need them, commonly right after the last time they're used:
-(UIImage *) downloadImageToFile {
// We own this object due to the alloc
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
// We don't own this object
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// We don't own this object
NSString *documentsDirectory = [paths objectAtIndex:0];
//[paths release] -- commented out, we don't own paths so can't release it
// We don't own this object
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
// We own this object due to the alloc
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[url release]; //We're done with the url object so we can release it
[data writeToFile:path atomically:YES];
[data release]; //We're done with the data object so we can release it
return [[UIImage alloc] initWithContentsOfFile:path];
//We've released everything we owned so it's safe to leave the context
}
Some people prefer to release everything at once, right before the context closes at the end of the method. In that case both [url release]; and [data release]; would appear right before the closing } brace. I find that if I release them as soon as I can the code is clearer, making it clear when I go over it later exactly where I'm done with objects.
To summarize: we own objects created with alloc, new, copy, or retain in the method calls so must release them before the context ends. We don't own anything else and must never release them.
¹There's nothing actually magical in the four words, they're just a consistently-used reminder by the folks at Apple who created the methods in question. If we create our own initialization or copying methods for a class of our own then the inclusion of the words alloc, new, copy, or retain in its appropriate methods is our responsibility, and if we don't use them in our names then we'll need to remember for ourselves whether ownership has passed.
One thing that helps me a lot is to have a breakpoint on objc_exception_throw. Anytime I'm about to get an exception thrown, I hit this breakpoint and I can debug back up the stack chain. I just leave this breakpoint enabled all the time in my iPhone projects.
To do this, in xcode go to near the bottom of the left pane "Groups & Files" and find "Breakpoints". Open it and click on Project Breakpoints and in the detail pane (top), you'll see a blue field labeled "Double-Click for Symbol." Double-click on it and enter "objc_exception_throw".
Next time you throw an exception, you'll stop and in the debugger, you can walk back up the stack chain to your code that caused the exception.
Your code shows a severe lack of knowledge of how memory management works in Objective-C. In addition to the EXC_BAD_ACCESS errors you're receiving, improper memory management also causes memory leaks which, on a small device like the iPhone, can lead to random crashes.
I recommend you give this a thorogh read:
Introduction to Memory Management Programming Guide for Cocoa
Definitely give memory management rules a quick review. Nothing jumps out that would cause the error you're getting, but you're leaking all those objects your allocating. If you don't understand the retain/release pattern, chances are there's another spot in your code where you're not retaining an object properly, and that's whats causing the EXC_BAD_ACCESS error.
Also note that NSString has methods for dealing with filesystem paths, you should never have to worry about the separator yourself.
In general though, if you're getting EXC_BAD_ACCESSs in your code and you can't for the life of you figure out why, try using NSZombie (no, I'm not kidding).
In Xcode, expand the Executables section on the left. Double click on the listing that has the same name as your project (it should be the only one). In the window that pops up, go to Arguments, and in the bottom portion, click the plus button. The name should be NSZombieEnabled and the value should be set to YES
This way, when you try to access a released object, you'll have a better of what you're doing. Just set the value to NO once you've figured out the bug.
Hope this helps someone!
These errors occur when you are mismanaging memory (ie. an object is being released prematurely or similar)
Try doing something like the following..
UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path];
return [myImage autorelease];
I spent a lot of time experimenting whilst getting to grips with the concepts of release/autorelease. Sometimes the retain keyword needs to be played also (although probably not in this case)
Another option could be simply the path does not exist, or cannot be read from?
Perhaps, initWithContentsOfFile doesn't take a path argument? Browse around at the different init methods for UIImage, I think there's a different one for accepting a path.
There also might be something fancier you have to do for making a path? I remember doing something with "bundles"? Sorry to be so vague, it's all I remember offhand.
take the slash off the path, and make sure it is in the project. doesn't matter if it is in that dir, but it has to be added to the project for you to access it.