views:

230

answers:

9

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
+9  A: 

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.

cobbal
Releasing `people` should probably be before he assigns it a new value. To me it looks like a global variable.
Georg
good point. It should be released (or autoreleased) somewhere though
cobbal
People cannot be released. It is used in cellForRowAtIndexPath. It is also a global variable. It is released in the Dealloc method.
Forrest
+1  A: 

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.

0A0D
+3  A: 

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.

Eric Petroelje
The biggest learning curve for me was learning(and still learning) when I should release or not. If you release an object from the API that you should you get EXC_BAD_CRAP but sometimes the documentation/header doesn't show this - You have to trust that everyone is keeping to naming conventions which personally I think sucks.
Chris Beeson
And to add is the biggest flaw in what is a lovely programming environment.
Chris Beeson
@Chris: I wholeheartedly disagree. Cocoa's wonderfully consistent and simple memory management rules are one of its greatest strengths. Short of garbage collection, I think the traditional Cocoa memory management paradigm is the nicest I've seen.
Chuck
@Eric: the problem is not in [path release]; or [propList release]; I only put those in because i saw it was a memory issue and started trying to find where i was leaking memory. i have since removed both of those statements
Forrest
+1  A: 

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

Justin
A: 

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.

Frank Schmitt
A: 

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.

coneybeare
A: 

what is self.editButtonItem? I don't see it in your .h file

Morion
That is commented out. That is code that is automatically placed there if u wish to implement and edit button on the nav bar
Forrest
A: 

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.

nall
+1  A: 

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.

coneybeare