iPhone SDK mem management issues - EXC_BAD_ACCESS - iphone

I've been staring at this same issue for a long time now, and would really appreciate any help or suggestions. I'm sure its something simple, but I can't seem to find it. In my app delegate I'm loading up a bunch of accessory objects (an object I created, which supports NSCopying) with the following code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Accessories" ofType:#"plist"];
NSDictionary *accDict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.colors = (NSArray *) [accDict objectForKey:#"Colors"];
self.exteriorAccessories = [self loadAccessoriesForMode:EXTERIOR_MODE withDictionary:accDict];
self.interiorAccessories = [self loadAccessoriesForMode:INTERIOR_MODE withDictionary:accDict];
[accDict release];
And this is the definition for the method its calling:
-(NSArray *)loadAccessoriesForMode:(NSString *)mode withDictionary:(NSDictionary *) dictionary
{
NSMutableArray *tempValues = [[NSMutableArray alloc] init];
for (NSDictionary *value in [dictionary objectForKey:mode])
{
Accessory *accessory = [[Accessory alloc] initWithDictionary:value];
[tempValues addObject:accessory];
[accessory release];
}
NSArray *returnArray = [[NSArray alloc] initWithArray:tempValues copyItems:YES];
[tempValues release];
[returnArray autorelease];
return returnArray;
}
When I get to the release for accDict I'm getting an EXC_BAD_ACCESS exception. If I take out the release of accessory inside the loop, everything is fine - but I'm leaking Accessory objects (which seems obv. to me - if I init it and I alloc it, its my job to release it).
When I step through this in the debugger, I'm seeing the init, copy and dealloc methods all fire on my Accessory object as expected. I can also post code for the Accessory object if you think it will help, but I think the problem is somewhere in this code.

I think I've found the cause, but I'll post it here so others can possibly benefit. It didn't really have anything to do with the code I posted. Rather the problem was inside of the Accessory object. I was setting things directly instead of calling the getters through self.
So this:
value = [dict objectForKey:#"myKey"];
Instead of this:
self.value = [dict objectForKey:#"myKey"];
Somehow this was causing me to have bad side effects on the NSDictionary itself (I thought that was not mutable, but it seems I was somehow messing things up). The only way I found this was to use the very helpful advice that I found on Cocoa With Love.
When I used the Print Description option in XCode, I was able to see that the NSDictionary somehow contained AccessoryValue objects - one of my custom objects that should NOT have been there since this was just loaded from a simple plist. Print Description can be found in XCode by hovering over the object to see its details (while the process is paused in the debugger) and clicking on the little up/down arrows right next to the triangle that expands into object details. For dictionaries, this will dump their entire contents to the console.

Please prefix this with an "I know nothing about objective C but":
It looks to me like you need to release the accessory items after you have copied them into the "returnArray", or maybe not specify "copyItems".

Run Clang on your code. It's a godsend. Clang rules! It will do Static Analysis of your code and tell you what you might be leaking. Great stuff.

I was battling with the Exc_Bad_Access issue for a day and finally found the answer. The issue was when I try to access one of the objects stored in an NSDictionary, the first time was OK but the second access the object turned to be nil even though the object counts in the dictionary remains the same. This strange behavior was due to releasing the object twice. Here is an example:
NSString* nstring=[[[NSString alloc]init]autorelease]
[AnNSDictonaryInstance setObject:nstring forKey:0];
...
[nstring release];
Notice that nstring was set autorelease then release again? It won't show problem right away unit you try to read the dictionary object the second times. I hope one day Apple's development team will be able to flag this as an violation while compiling.
I hope this post will help someone out.
Wayne of Campbell

Related

Objective-C: detect if an object has no more holders

I use an NSMutableArray as a "Window Manager" to store all my NSView objects, I send it like
[windowManager addObject:self];
right after ViewDidLoad of each NSView;
the question is, how can I detect on my "windowManager" NSMutableArray if it stores objects that has been called to release (and windowManager is the only one holding their instance)?
After I call a release upon an one of these NSViews and turn
SomeNSViewPointer = nil;
, I also need the WindowManager to check the above.
When you do this:
[someNSViewPointer release];
SomeNSViewPointer = nil;
Do this instead:
[someNSViewPointer release];
[windowManager removeObject: someNSViewPointer];
SomeNSViewPointer = nil;
I would have to question why you are storing two references to your view though. I think your design needs reviewing.
You can send the view you are checking the -(int)retainCount message and check if it is one (ie the single retain from adding it to the array)? This question raises a red flag that you are doing soemthing that might be considered bad design, why are you doing this?
When you add anything to your NSMutableArray via 'addObject', that NSMutableArray does a retain on it. You will need to remove that object from the NSMutableArray to have it released, if that is what you are after.
[yourNSMutableArray removeObjectIdenticalTo:yourNSView];
Maybe try if([myView retainCount] == 1), then it is only retained in the windowManager.
[EDIT] Read about retainCount in NSObject reference. As JeremyP suggests, it is not smart to use this in your code, but think better of your code design.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/retainCount

Why does this code produce leaks?

A total Noob here positing for the first time on this forum.
I am writing my very first iPhone app. The code in question is to populate my UITableViewCell in the cellForRowAtIndexPath method.
When I run my app with Leaks Instrument turned on, the leaking part points to this part of my code. I have read about memory management on various books and postings on this site and per my knowledge the code below seems to conform to the required alloc/release coding guidlines. However, the Leaks tool still points to this particular section. I simply don't understand why.
Your help would be appreciated.
ModelClass * myModelData = [myTempDataArray objectAtIndex:[indexPath row]];
NSString * myURL = [[NSString alloc] initWithFormat:#"%#/images/%#", SERVER_URL, [myModelData publisherLogoFileName]];
NSURL * imageURL = [[NSURL alloc] initWithString:myURL];
[myURL release];
[[cell productImageM] setUrl:imageURL];
[imageURL release];
[[self objMan] manage:[cell productImageM]];
return cell;
The Leaks tool doesn't tell you where the object should have been released, it tells you where it was allocated.
That bit of code looks fine; you own myURL long enough to use it to create imageURL and then release it. You give imageURL to the cell's productImageM and then release it. You then do some manipulation on objects you don't own.
At a guess, the type of object returned by [cell productImageM] retains its Url property and subsequently leaks it.
You can try to use the Build & Analyze option from the Build menu. In general it should indicate the exact line of the leak.

how are these NSMutableArray initializations different?

In a branch of my code, I previously used this
NSMutableArray *array1 = [[NSMutableArray alloc] init];
The above array is used populate a UITableVew.
Just cause, I switched to the following:
NSMutableArray *array1 = [NSMutableArray arrayWithCapacity:0]
I made no other changes to my code) and my app crashes whenever I try to scroll down the list in the UITableView.
It looks like my array is not initialized correctly. Can someone explain why this would happen? Are the two methods not identical wrt how the underlying memory space is allocated?
Your second line of code is not retaining the NSArray, which is causing a crash. You'll need to call [array1 retain] after you call arrayWithCapacity:.
There's quite a bit of useful information in this post: Understanding reference counting with Cocoa / Objective C
In general, if you're calling a class method that doesn't start with "new" or "init" (e.g. arrayWithCapacity), you can usually assume that the returned object will be autoreleased.

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.

NSMutableDictionary throws a doesNotRecognizeSelector to objectForKey?

I'm a total noob to iPhone programming, and I've run into an exception being thrown that I just can't wrap my head around.
Background: The error is happening in a custom subview, and occurs immediately upon loading the program. I'm getting an exception thrown in the overridden drawRect method. The code throwing the error follows:
- (void)drawRect:(CGRect)rect{
NSNumber *points = [NSNumber numberWithInt: star.numberOfPoints];
//HERE. Doesn't recognize selector?!
CGPathRef t = (CGPathRef)[starPaths objectForKey:points];
/*snip*/
starPaths is initialized in awakeFromNib as an NSMutableDictionary with capacity 1.
The exception that's getting thrown is -[NSObject doesNotRecognizeSelector:]
starPaths is declared in the header file for the view as
NSMutableDictionary *starPaths;
and is initialized as
- (void)awakeFromNib{
starPaths = [NSMutableDictionary dictionaryWithCapacity: 1];
}
Finally, I haven't been able to get to a point in the code where I successfully add elements to the dictionary, since the code to add an entry relies on receiving a nil response from the dictionary to know that the that specific entry needs to be built.
Any suggestions? Any other information I should provide?
Any help at all would be appreciated, I feel like I'm missing something obvious, but I've been bashing my head against this all day with no luck.
If you do not retain the starPaths variable or explicitly allocate it yourself with [[NSMutableDictionary alloc] initWithCapacity:1] then it will be automatically released on the next iteration of the run loop.
You need to do
starPaths = [[NSMutableDictionary dictionaryWithCapacity:1] retain];
Or
starPaths = [[NSMutableDictionary alloc] initWithCapacity:1];
Just make sure to release it when you no longer need it.
A few things to check:
Is starPaths declared as an NSMutableDictionary* instance variable? You mention that it is initialized. Did you use the dictionaryWithCapacity method (which returns an auto-release object) or initWithCapacity (which needs to be explicitly retained). To be safe, you may want to retain it and release it when done.
Double-check to make sure the header files are properly included so the declaration of starPaths is included in implementation files that make use of it.
Generally speaking if you're getting mystery errors it has to do with corrupted memory. Try putting a breakpoint on the first line of the drawRect method and do a "po starPaths" in the debugger console window to see what's in it and what type of object the runtime thinks it is.