how should I release this NSString correctly? - objective-c

Code:
- (void) foo : (NSString*) ori_string
{
her_string = [ori_string copy];
while ([her_string length]>0)
{
her_string = [her_string substringFromIndex:1];
//do something...
}
[her_string release]; //Here is the problem
}
Hi all,
if I release her_string like above, the Analyzer said that it's an incorrect decrement of the reference count of an object that is not owned at this point by the caller.
Otherwise if I don't release it, it said that it's a potential memory leak.
Where and How should I release it? Thank you!

Remove the [her_string release] line, and add autorelease to the copy.
- (void) foo : (NSString*) ori_string
{
her_string = [[ori_string copy] autorelease];
while ([her_string length]>0)
{
her_string = [her_string substringFromIndex:1];
//do something...
}
}
The issue is that the copy returns a string that must be released, and you lose the reference to it by overwriting the string with substringFromIndex calls. After losing the reference it can never be properly released and thus the first copied version of the string leaks (if length > 0, otherwise your code properly releases the string).
substringFromIndex returns an already-autoreleased string, so you don't have to worry about it until you want the string to persist outside of the current autorelease pool.

you don't have to release NSString returned by [NSString copy]
you only release object that is created by [[XXXX alloc] init]
IOS 5 use ARC, you never need to worried about when to release or retain if you work with ARC

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];

Does ARC ever inject unconventional code?

Does ARC ever inject retain and release calls that you generally wouldn't see in a non-ARC environment?
For example, explicitly releasing an object from a getter:
- (NSArray *)dummyArray {
return [[NSArray alloc]init];
}
- (void)useDummyArray {
NSArray * arr = [self dummyArray];
//do something with arr
[arr release]; //unconventional injection of release.
}
Would ARC ever generate a release statement like the code above or would it autorelease the array returned by [self dummyArray];
The beauty of ARC is that you don't know, or need to know. However, you can give hints to the ARC static analyzer:
-(NSArray *) dummyArray NS_RETURNS_RETAINED { // this tells ARC that this function returns a retained value that should be released by the callee
return [[NSArray alloc] init];
}
-(NSArray *) otherDummyArray NS_RETURNS_NOT_RETAINED { // this tells ARC that the function returns a non-retained (autoreleased) value, which should NOT be released by the callee.
return [[NSArray alloc] init];
}
However, NS_RETURNS_NOT_RETAINED is the default, as long as your function name doesn't begin with init, in which NS_RETURNS_RETAINED becomes default.
So, in your specific scenario, it will almost always return an autorelease'd value. One major reason for this is support for interpolation with non-ARC code, which could result in leaks.

autoreleasing NSString in class method causing app crash in iOS

The error I receive is as follows:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); //breakpoint that says Thread 1: Program Received Signal: "EXC_BAD_ACCESS".
[pool release];
return retVal;
}
My two questions can be found at the bottom of this post :)
I am currently working on an assignment for an iOS programming class and have hit a road bump.
I have found a fix, shown below, but it doesn't make sense to me. Check it out:
#implementation MyClass
// This class method takes an (NSMutableArray *) and returns an NSString with its contents printed out.
+ (NSString *)myString:(NSMutableArray)anArray
{
// NSString *myString = [[NSString alloc] init]; OLD CODE THAT CAUSES MEMORY LEAK
NSString *myString = [[[NSString alloc] init] autorelease]; //NEW CODE THAT RELEASES FIRST ALLOCATION OF myString WHEN THE FIRST stringByAppendingFormat: IS CALLED
NSString *vp = VARIABLE_PREFIX; //#defined above to be #"%
for (id object in anArray) {
if ([object isKindOfClass:[NSString class]]) {
if ([object hasPrefix:vp]) {
myString = [myString stringByAppendingFormat:#"%#",[object substringFromIndex:1]];
}else{
myString = [myString stringByAppendingFormat:#"%#",object];
}
}else if ([object isKindOfClass:[NSNumber class]]) {
myString = [myString stringByAppendingFormat:#"%#",object];
}
}
return myString; //shouldn't I autorelease myString right before this line? NO NOT ANY MORE. THIS myString IS NOT THE ORIGINAL THAT I alloc-init, BUT AN AUTORELEASED OBJECT RETURNED BY THE stringByAppendingFormat: message.
}
When I try to send the message [myString autorelease];, the program crashes with the above error. It is working fine now as shown above, but I do not understand why.
Every time I send a message containing the "magic words" alloc, init, copy I have to call release, it don't I? Or are the rules different in a Class method (can the Class itself own a file?). I do not call retain in the object that is calling this file.
Here are my two questions:
Why does this crash when I try to release theDescription using autorelease?
Does my code create a memory leak?
This is my very first question on stack overflow! Thank you for your help!
Why does this crash when I try to release theDescription using autorelease?
Assuming you mean myString, it crashes because myString is already autoreleased. You got it by calling -stringByAppendingFormat:, which returns an autoreleased string. Now, you're probably thinking: "But I created it by calling +alloc, so I should release it." That's true, but NSStrings are immutable, and when you call -stringByAppendingFormat: you get a different string back, and that string is autoreleased. Autoreleasing it a second time is an error.
Does my code create a memory leak?
Yes, but not really. The "leaked" object is the empty string that you allocate in the beginning. You never release that string, so you've got a leak. However, NSString is apparently optimized so that [[NSString alloc] init] returns a singleton, so in this particular case it doesn't make any difference that the empty string isn't released. The other strings that are assigned to myString are all autoreleased, so none of those objects are leaked.
Why does this crash when I try to release theDescription using
With the updated code, the problem is that ypu are reassigning the pointer to myString using the methid which already returns an autoreleases object: stringbyappending, therefore if you call autorelease on this object which is already going to get autoreleased i will crash.
Aditionaly the first assugnment in the alloc init gives a memory leak when ypu reassign with stringbyappendingstring, since you lose the reference to the previously created string with alloc init and therefore you will never be able to release it.
Sorry for the formatting on my iPhone atm =)

Objective-C memory management: how do you release the memory when you return an allocated instance?

How do you release the memory in this situation? Do you have to?
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
// ....
// more code ...
// ...
return mutableString;
}
With autorelease
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
[mutableString autorelease];
return mutableString;
}
As willcodejavaforfood said, the convention is that any newly-allocated object returned from a method should be autorelease'd before being returned. This tells the Objective-C runtime that if nobody takes ownership of it with retain before (in most cases) the current iteration of the application event loop ends, it should be freed.
If it's just used locally in the calling function or returned up the stack, that works great and it gets freed sooner or later. If someone ends up wanting to keep it around, then they have to retain it and so will know they have to release it themselves later on.
(In fact, most of the non-init* utility constructors for ObjC base classes like strings, etc, do just that, or at least something functionally equivalent.)

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.