EXC_BAD_ACCESS when setting ivars directly (without using accessors) inside -init method, why? - iphone

I've spent about 10 hours trying to find this bug that was causing my app to crash, and it was in the last place I looked (well it would have been, but last place I ever expected it to be).
Originally I thought I had memory management issues (unbalanced retain/release) because the crash would happen every time I sent -removeAllObjects to an NSMutableArray filled with my custom objects. The crash wouldn't happen the first time -removeAllObjects was called. I could clear the array once, repopulate it, and then on the second clear, I would get a EXC_BAD_ACCESS. This is when my array got populated with 3 objects on the first "cycle", and 3 again on the second "cycle". When I was storing only 1 object in the array in each cycle it took 4 cycles to crash (on the 4th call of -removeAllObjects).
I FINALLY realised that the crash would go away if I changed the -init method of my custom object. Here's the -init implementation; all 4 ivars are synthesized properties with (nonatomic, retain), all of type (NSString *) except for icon which is an (NSNUmber *)
-(id)init {
if (self = [super init]) {
ip = #"";
mac = #"";
vendor = #"";
icon = [NSNumber numberWithInt:0];
}
return self;
}
Changing it to this fixed the bug:
-(id)init {
if (self = [super init]) {
self.ip = #"";
self.mac = #"";
self.vendor = #"";
self.icon = [NSNumber numberWithInt:0];
}
return self;
}
I've read that one should't use the accessors in the -init method because it can cause trouble (e.g. with subclassing).
If someone could explain to me why my bug goes away when I use accessors I would be so incredibly grateful! Seriously this has been driving me nuts, was up till 5am last night because of this.

You are assigning, but not retaining, the instance variables directly. When you use the dot syntax, you are triggering the retain part of the synthesized property and, thus, are retaining them.
-(id)init {
if (self = [super init]) {
ip = #"";
mac = #"";
vendor = #"";
icon = [[NSNumber numberWithInt:0] retain];
}
return self;
}
That should likely fix the problem (though, I'm slightly surprised, I thought 10 was still in NSNumber's instance cache. Maybe not.).
Technically you should also retain the #"" strings, but you can get away with not doing so because such strings are a special cased constant string that comes out of the compiled executable (as a private subclass of NSString that overrides to not respond to retain/release/autorelease).
The memory management guide covers this in detail. For anyone new to the platform, I would suggest re-reading it once a month (no, really -- interleaving your coding with an occasional re-read of the docs will often reveal subtle details that you didn't have enough experience to grok before. I still re-read the basic guides on a semi-annual basis.)

Related

Objective-C Memory Management: crash on release

I'm new to Objective-C and can't seem to get the memory management code right. I have the following code:
Media* myMedia = [self.myMediaManager getNextMedia];
self.navigationItem.title = [self.myMediaManager getCategory];
[self.btnImage setImage:myMedia.imageFile forState: UIControlStateNormal];
[self.lblImage setText:myMedia.imageLabel];
//[myMedia release];
My app crashes if I uncomment the above line. Do I need to do something special when I instantiate myMedia?
EDIT:
If myMediaManager is supposed to release it, when would it do that. Here is my code for getNextMedia:
- (Media*) getNextMedia {
DLog(#"Start");
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
return nextMedia;
}
EDIT2: I fixed the crashing issue (I was releasing an object I didn't own). I still see leaks and can't seem to find what the issue is.
Only objects that you own can be released.
You can release objects if you new, alloc, copy, mutableCopy or retain them first.
Since there is no alloc/copy/retain in [self.myMediaManager getNextMedia]; you can't release it.
Since myMedia is not retained here, you don't need to release it. When the origin (self.myMediaManager) releases it, it gets destroyed immediately.
NSString *string = [[NSString alloc] init];
[string release]; // now we have to release the string, since we allocated it.
NSString *string = self.navigationItem.title;
// now we don't, since it's a property of `navigationItem` and we didn't retain it.
At this point, since you are just learning, you should probably just start off using ARC with the iOS5 beta versions of XCode. It's good to have an understanding but using ARC will save you many potential pitfalls - by the time you learn enough to produce something iOS5 will be out. You can still build applications targeting iOS4, so you'll still be able to reach a lot of people.
The general rule for memory management is as follows:
For every retain, alloc, copy, or new, you need to call release or autorelease.
Since you did not call any these, you do not need to release myMedia.
For more information, take a look at this other answer I posted that deals with the subject. Also, since you are new to iOS development, I suggest looking at this answer as well.
This updated code is suspicious:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
Depending on the condition in the if() clause, you assign a new value to nextMedia, which makes the value you just allocated unreachable, i.e. it can't be released.
Also, you don't retain the value you get from the array, so you should not release it either. But if the if() clause does not run, you still have the instance you alloc-ed, and that should be released.
That is not good. Try:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
[nextMedia release];
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
You could also do (and I would prefer that):
Media *nextMedia;
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
else
{
nextMedia = [[Media alloc] init];
}
Now you can release nextMedia when that is not needed anymore, without any ambiguity about the retain count.

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

iPad large NSArray - initWithObjects vs. ArrayWithObjects

can anyone clear this up for me ?
I am building an iPad App that has a TableViewController that is supposed to show something between 1000 and 2000 strings.
I have those NSStrings in a Singleton.
In the init Method of the Singleton I initialize an Array that holds all the data ( does not have to be the final way to do it - was just a quick copy and paste for testing )
I did an self.someArray = [[NSArray alloc]initWithObjects: followed by the large number of strings, followed by nil.
that worked fine in the simulator - but crashed with bad access on the iPad right on Application startup
If I use the convenience method [NSArray arrayWithObjects:instead - it works fine.
I looked into Instruments and the overall memory footprint of the App is just about 2,5 MB.
Now I don't know why it works the one way but not the other.
EDIT:
#import "StaticValueContainer.h"`
static StaticValueContainer* instance = nil;
#implementation StaticValueContainer
#synthesize customerRatingKeys;
+(StaticValueContainer*)sharedInstance
{
if (instance == nil){
instance = [[StaticValueContainer alloc]init];
}
return instance;
}
-(id)init
{
if ( ( self = [super init] ))
{
[self initCustomerRatingKeys];
}
return self;
}
-(void)init customerRatingKeys
{
self.customerRatingKeys = [[NSArray alloc]initWithObjects:
#"string1",
....
#"string1245"
,nil
}
as I said: it crashes on the device with self.customerRatingKeys = [[NSArray alloc]initWithObjects:
but works with *self.customerRatingKeys = [[NSArray arrayWithObjects...`
Well, there isn't much difference between them: arrayWithObjects returns an auto-released array that you don't need to release yourself (unless you subsequently retain it), and initWithObjects returns an array you must then release to avoid a memory leak. Performance wise there is no difference between them.
I would suggest if you're getting a bad access error using initWithObjects but not with arrayWithObjects there might be some sort of memory management error in your code. If you post the code itself you'll probably get a more exact response.

EXC_BAD_ACCESS in iPhone app - memory management issue

For reference, I've already read:
memory management question -- releasing an object which has to be returned
iPhone: Return NSMutableArray in method while still releasing
Releasing objects returned by method
Which I thought would help :).
This app is a teaching tool and is intended to help people visualize simple genetics. Just some background so the variable names and stuff will make sense. Here's the main code that executes when the app runs:
- (void)viewDidAppear:(BOOL)animated {
ThingzCore *core = [[ThingzCore alloc] init];
ThingzThing *thing1 = [[ThingzThing alloc] init];
thing1.breed = #"AB";
thing1.pattern = #"AC";
thing1.color = #"CA";
thing1.gender = #"XY";
thing1.generation = 1;
thing1.isEgg = NO;
ThingzThing *thing2 = [[ThingzThing alloc] init];
thing2.breed = #"CD";
thing2.pattern = #"BA";
thing2.color = #"CB";
thing2.gender = #"XX";
thing2.generation = 1;
thing2.isEgg = NO;
NSLog(#"Breeding GD BR PT CL G");
ThingzThing *child = [core mateFather:thing1 withMother:thing2];
NSLog(#"Round 1: %# %# %# %# %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(#"Round 2: %# %# %# %# %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(#"Round 3: %# %# %# %# %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(#"Round 4: %# %# %# %# %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
[thing1 release];
[thing2 release];
[core release];
}
And here's what happens when I run it in various ways:
Running without breakpoints, it crashes, with no console message, after the 2nd sleep() but before the "Round 3" NSLog.
Running with breakpoints enabled, but none defined, it runs through the entire sequence. After the fourth sleep(), it crashes with EXC_BAD_ACCESS.
Running with breakpoints enabled and NSZombiesEnabled, it does the same thing as above - no further information, just EXC_BAD_ACCESS.
Running in Instruments, no leaks are shown.
This is the routine being called four times:
-(ThingzThing *)mateFather:(ThingzThing *)father
withMother:(ThingzThing *)mother {
// will we be doing a mutation?
int mutationPercentage = father.generation + mother.generation;
int mutationNumber = (arc4random() % ((unsigned)100 + 1));
BOOL isMutation = NO;
if (mutationNumber <= mutationPercentage) {
isMutation = YES;
}
// get possibilities
NSArray *possibilities = [self possibilitiesByMatingFather:father
withMother:mother
mutations:isMutation];
// randomly select one of the possibilities
int keeping = (arc4random() % ((unsigned)[possibilities count]));
return [possibilities objectAtIndex:keeping];
}
Without pasting in the ENTIRE code, the possibilitiesByMatingFather:withMother:mutations function is returning an NSMutableArray. That routine declares the array by using:
NSMutableArray *possibilities = [NSMutableArray array];
It then:
return possibilities;
It does not send a release or autorelease message to possibilities; my understanding is that creating the array the way I have is an implicit autorelease. I didn't want to alloc the array, because I'm returning it, so wouldn't have the opportunity to explicitly release it.
The objects held in the possibilities NSMutableArray are of a custom class. They are added as follows:
ThingzThing *newThing = [[ThingzThing alloc] init];
newThing.breed = choiceBreed;
newThing.gender = choiceGender;
newThing.color = choiceColor;
newThing.pattern = choicePattern;
newThing.generation = mother.generation + father.generation;
newThing.name = #"";
newThing.isEgg = YES;
[possibilities addObject:newThing];
[newThing release];
Which seems to work most of the time. At least, when breakpoints are enabled, the program runs through the code without complaint until the end, as noted above.
Any suggestions on what I'm doing wrong, here? It's obviously memory management issues of some kind, but I can't sort it in my head.
BTW, in a vain, throwing-things-at-the-wall attempt to figure it out, I did modify the one line from the main routine as follows:
// get possibilities
NSArray *possibilities = [[self possibilitiesByMatingFather:father
withMother:mother
mutations:isMutation] retain];
To no avail. Same results. So the problem isn't in retaining the array returned by possibilitiesByMatingFather:withMother:mutations. Forcing a retain on that return isn't helping.
Frequently in this type of situation, the actual error is not at the location shown in the debugger when the app halts.
For example, the debugger may be pointing to a method call, but the problem actually occurs inside the method -- not when the method is called.
To track things down, I would suggest setting a breakpoint just before the method call that triggers the error -- in your case, this would be the mateFather: withMother: call. Then step into that method. There is a good chance you will find that the problem happens inside that method -- or even inside a method called from within that method.
Check you have the correct property declarations of the string properties of class ThingzThing.
e.g.
#property (nonatomic, retain) NSString* breed;
NEED to be retain or copy NOT assign...
Found it. Buried eight method calls down, I was sending release to an NSArray that was obviously autoreleasing. When the initial calling routine (viewDidAppear) fell down into autorelease mode, it tried to autorelease the already-released object, and exploded. Good grief - is there any way XCode could have helped me track that down?
In any event, in case anyone runs across this, make bloody sure you're not sending a release message to something that you didn't explicitly alloc yourself. If you didn't alloc it, odds are it was autoreleasing itself, and you sending a release to it takes the retain count negative, and the Foundation framework vomits with an EXC_BAD_ACCESS.

Objective C NSString being released in singleton

I'm constructing a small iphone app and using a singleton to store and update a string that gets updated when the user taps letters or numbers on the screen to form a code.
i.e. they tap 3 then S then 4 and I need to track and combine that input to give me "3S4" say. When the singleton is initialised it creates an empty NSString and I then use the stringByAppendString method to add on the next letter/number tapped. When I first tried this I did not have the [enteredCode retain] line in there and the app would crash with EXC_BAD_ACCESS, always after 2 inputs. I set the NSZombie property which told me that the enteredCode had been de-allocated but I don't know where or how that happened. All I know is that at the end of the addInput method it will report the retainCount to be 2 say and then straight after I can see (by calling the singleton from elsewhere) it will drop down to 1 (when the retain line is in there).
My question is: though what I've done by adding [enteredCode retain] works for me am I breaking some rules here or going about this in the wrong/bad way? I just can't see why the string is being released.
I'm new to Objective-C btw
in MySingleton.h
#interface MySingleton : NSObject {
NSString *enteredCode;
}
in MySingleton.m
-(void) addInput:(NSString *) input
{
NSLog(#"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
enteredCode = [enteredCode stringByAppendingString:input];
NSLog(#"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
[enteredCode retain]; // without this the app crashes
NSLog(#"enteredCode retain count is : %d \n ",[enteredCode retainCount]);
}
-(id) init
{
self = [super init];
if (self)
{
enteredCode = #"";
}
return self;
}
First, never use the -retainCount method. The absolute count of retains on an object is an implementation detail of the frameworks and will often return confusing results.
Retain counts are something you should maintain entirely as a balanced set of deltas. If you cause a retain count to be added to something, you must release or autorelease that object somewhere. End of story.
This document explains it all.
With that knowledge in hand, the source of your crash is a fairly common memory management mistake.
enteredCode = [enteredCode stringByAppendingString:input];
Every time that line of code is executed, you are replacing enteredCode with an autoreleased instance of NSString. The autorelease pool is drained and your program crashes the next time enteredCode is used.
Your solution of retaining enteredCode is only half the solution. You need to ensure that the original value of enteredCode is released, too. See the memory management docs.
If this were my app, I would turn enteredCode into an #property that copies the string and always set and access enteredCode through that property, never retaining or releasing it manually in my code (outside of -dealloc, of course).
NSString's stringByAppendingString: returns a new NSString made by appending one string to the other, and the new NSString is set to autorelease, which empties the autorelease pool and your next run crashes the app. You're redefining an existing string with stringByAppendingString:, and that's causing the retain problems. (Alternatively, use NSMutableString and you can avoid this.)
By the way, you can do if (self = [super init]) in your init override. The declaration returns true if it occurs or can occur.
Here's how your code should look:
#interface MySingleton : NSObject {
NSString *enteredCode;
}
#property (nonatomic, retain) NSString *enteredCode;
#end
#synthesize enteredCode;
-(void) addInput:(NSString *) input
{
self.enteredCode = [self.enteredCode stringByAppendingString:input];
}
- (void)dealloc {
[enteredCode release];
}
#end