Confused about alloc and release - objective-c

I'm a little confused about Objective-C and allocating/releasing objects.
If I do this:
NSString *myString;
if([someString isEqualToString: #"test1"]){
myString = #"got 1";
}else{
myString = #"got 2";
}
Do I have to release myString after that?
And the same with self-defined objects:
myOwnObject *someObject = [someArray objectAtIndex: 1];
myButton.label1.text = someObject.name;
Do I have to release someObject?
The reason why I'm asking is that I get memory-leaks in a method and I can't find where it is. So I'm trying to figure out whether I do the alloc/release stuff correctly.
The leak occurs on a NSPlaceholderString (I guess that's somewhere hidden in my NIB-File).
Also - if I have an object, allocate it, but only use some of the properties, but DO a release of every property on dealloc - will this cause memory leaks?
Sorry - hope my questions do make at least some sense :)
Thanks for any help!

Listen to me. THIS IS THE ONLY RULE THAT MATTERS.
If you use a method with "copy", "alloc", "new", or "retain" in the name
You own the object and MUST later release or autorelease it.
If you don't:
Don't!
But don't expect the object to stick around outside of that scope, because you don't own it.
It's that simple.
MyClass *foo = [[MyClass alloc] init];
[array addObject:foo];
[foo release];
Did you use "copy", "retain", "new", or "alloc"? Yes. Release it.
MyClass *someObject = [someArray objectAtIndex:0];
Did you use "copy", "retain", "new", or "alloc"? No. Don't release it.
BUT
If you have an instance variable which you need to access in other methods:
ivar = [[someArray objectAtIndex:0] retain];
Then you're guaranteed it will stick around because you own it.
(Another way to handle this is with #property (retain) properties, because then you can do self.ivar = someObject and it'll retain it for you.)
But remember to release them in -dealloc!

No, you don't have to release either of those. I usually release only objects that I alloc, such as this snippet:
NSString *string = [[NSString alloc] initWithFormat:#"%#", something];
// yadda yadda yadda some code...
[string release];

To answer your first question, you don't need to release strings created with the #"" syntax.
On your second example, you should not have to release someObject. However, a problem could arise if your dealloc method in your myOwnObject class does not correctly release all of its instance variables.

Related

Retain and release nsstring

str is a NSString I own. It is my responsibility to release it,
since I call initWithString:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
str = [str stringByAppendingString:#"\n"];
}
if the line inside the if statement reached, I will lose the ownership of the str var.
so my app crashes with a zombie instance, when I release str later:
[str release];
All goes fine if the if statement is NO(false).
What can I do to maintain the ownership of str?
Note that str could be very long I don't want to init another NSString
You need to do normal memory management:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
NSString *newStr = [str stringByAppendingString:#"\n"];
[str release];
str = [newStr retain];
}
Keep in mind that stringByAppendingString: returns an autoreleased string (and it also creates a whole new string).
Suggestion 1: Use ARC. It solves these problems for you. This is by far the best solution.
As rmaddy says, Xcode has an automated tool for converting apps to ARC. Look in the edit menu, under refactor>Convert to Objective-C ARC. The process is fairly painless. It flags things it wasn't able to figure out on it's own (usually only a few things.) After you clean up those issues you are off and running and never have to worry about retain counts again.
Suggestion 1a: Make str a mutable string, as #rmaddy suggested.
Then your code would look like this:
[str appendString: #"\n"];
That's simpler, easier to read, more memory-efficient, and works exactly the same in both ARC and manual reference counting.
Failing that, change str to be a retained property
#property (nonatomic, retain) NSString *str);
Then use property notation:
if (![[self.str substringFromIndex: self.str.length-1] isEqualToString:#"\n"])
{
self.str = [self.str stringByAppendingString:#"\n"];
}
When you do that the setter for the property takes care of releasing the old object in the str property before assigning a new value to the property.
Be aware, though, that assigning an object to a retained property increases it's retain count. This will create a leak:
self.str = [NSString alloc] initWithFormat: #"number %d", value];
(because all alloc/init calls return objects with a retain-count of 1)
and then the property retains it again.
That code should be written like this:
self.str = [[NSString alloc] initWithFormat: #"number %d", value] autorelease];

Objective-C Property assignment without #property

I'm currently developing an iOS application which was started by another developer.
Usually, I make a property for every instance variable (assign for int, bool etc. / retain for all classes).
So in my projects, this line causes a leak:
myVar = [[NSString alloc] init]; (alloc/init +1, retain in setter +1, release in dealloc -1 => +1)
So I use:
NSString *tmpMyVar = [[NSString alloc] init];
[self setMyVar: tmpMyVar];
[tmpMyVar release];
Or:
NSString *tmpMyVar = [[[NSString alloc] init] autorelease];
[self setMyVar: tmpMyVar];
In this new project, the previous developer didn't use #property/#synthesize so I'm wondering what will be the result of the previous line of code in this context (it doesn't call setter I guess)? Memory Leak?
The previous developer releases variable in dealloc method, just like me.
Thank you very much!
Since it directly assigns the instance variable to the allocated object it's retain count is 1 (because, like you said, a setter isn't called).
And because it's released in dealloc, it's all balanced out. So no memory leaks.
So in my projects, this line causes a leak:
myVar = [[NSString alloc] init]; (alloc/init +1, retain in setter +1, release in dealloc -1 => +1)
No,it wouldn't even in your projects, because, as you pointed out, no setter is used.
Also, when using properties, it is the recommended way to access instance variables directly in the init method, instead of using setters.
To inspect for questionable memory-leaks like your example, also use the clang static analyzer or instrument's leak tool.
You need to look at the other developer's setter implementation. Make sure they release the existing value and retain the new value; something like:
- (void)setMyString:(NSString *)string
{
[string retain];
[_string release]; // ivar
_string = string;
}
The only advantage to implementing your own setter/getter methods is to do something (other than setting the ivar) when a value is set. If the methods don't do anything like this then why not change all implementations to #property/#synthensize?

Regarding memory management in Objective C

According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!

Difference bewteen declaring a NSString with alloc and init and assigning with #"myString"

Currently I'm troubled with a small understanding issue on Objective-C. As example following Aaron Hillegass's book, I'm wondering about assigning an NSString in the init method of a class a value like in this example (For people who know the book, this is used in the Person class of RaiseMan):
- (id)init
{
if(![super init])
return nil;
myString = #"New entry";
return self;
}
This string isn't allocated by me, so normally I shouldn't bother about releasing it.
BUT! What happens in a setter-method of this string? Following the memory management rules the method should look like:
- (void)setMyString:(NSString *)newString
{
if(myString != newString) {
[myString release];
[newString retain];
myString = newString;
}
}
Why does [myString release] work? I've read somewhere, that with = #"bla" assigned strings can't be released.
And is initializing with = #"bla" the right way? Or should I use alloc and init instead?
Thanks for any help :)
NSString *constantString = #"constantString";
String like constantString are said to be from a private(?) class NSConstantString and they are alive through all your program life. Off-course release and retain work, (in the mean that they won't give you a exception or crash) They just do nothing.
Read more here
Also you said in one of your comments that it would be a{#property(..., copy) NSString myString;But what you are showing us is a typical #property(..., retain)
AFAIK, string constants of the form #"..." are actually a child class of NSString that redefine retain and release as no-ops. This allows the compiler to store those string constants in the data segment of your executable instead of on the heap.
Is myString declared in the header-file? Like: #property(nonatomic, retain) NSString myString. If that is the case, then myString is retained. Otherwise, it's not necessary to release it.

Overreleasing issue and Zombies

this program crashes if i uncomment the release statements. i get that i am overreleasing and realized that quickly. but, just to test zombies, i turned them on (NSZombiesEnabled = YES and CFZombieLevel = 16) and the program runs fine and throws no exceptions.
what gives? i thought turning on zombies would have just told me what a doofus i am...not fix it.
#import "AppController.h"
#implementation AppController
-(IBAction)countCharacters:(id)sender {
//did a button do this?
if(![sender isKindOfClass:[NSButton class]]) {
NSLog(#"%# is not a button", sender);
return;
}
//proceed
NSString *userString = [textField stringValue];
NSNumber *count = [NSNumber numberWithInt:[userString length]];
NSString *outputString = [NSString stringWithFormat:#"'%#' has %# characters.",
userString, count];
//[userString release];
//[count release];
[labelField setStringValue:outputString];
//[outputString release];
}
#end
It's because you do not own the objects you try to release (you don't hold a reference to them). Their ownership is given to the "nearest" NSAutoreleasePool.
You can read about object ownership here. As a quick reference, usually, you aren't the owner if you didn't call the alloc method yourself to create the object or if you didn't retain it. Retaining an object makes you an owner; calling release means you give up ownership (and will deallocate the object if it has no more owners).
You must not release objects for which you don't have ownership. Your current code without release is exactly what you need.
Well, zombies will tell you when a free'd object receives a release correct? So, if your not sending the release (you commented it out) you won't see the zombies complaining?
Your NSString/NSNumber methods are all convenience methods, and you don't have to release them. So -yeah, You solved the problem yourself.