I have declared a local string inside the method. I am releasing the string inside the same method. I found my code crashing if release that object. If I don't release the string, code runs successfully.
I have called that method in viewdidappear so that method is called while pushing and poping. Nothing gets printed in the console. Here is my code
-(void)appendString{
NSString *locStr = [[NSString alloc] initWithString:#""];
for (int i=0;i<[result count]; i++)
{
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
[locStr release];
}
I am calling the "appendString" method from "viewDidAppear"."str" is a NSMutable string declared in .h class.How should I release the "locStr".
What went wrong in my code? This isn't the normal way to release it?
Try this:
-(void)appendString{
//stringWithString returns an autorelease object
//so you don't need to worry about its memory management
NSString *locStr = [NSString stringWithString:#""];
for (int i=0;i<[result count]; i++)
{
//if your locstr is created by initWithString instead,
//the following line is going to cause memory leak
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
//[locStr release];
}
And make sure that the string property in your str instance is set to retain
You may want to use NSMutableString and not create multiple string objects that may or may not release at some time in the distant future.
-(void)appendString
{
NSMutableString *locStr = [[NSMutableString alloc] initWithString:#""];
for (int i=0;i<[result count]; i++)
{
[locStr appendFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
[locStr release];
}
see
In your code, calling [locStr release] is sent to the NSString instance returned from [locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]] That code actually returns a new String instance, so your variable locStr is now pointing to that new NSString instance and your reference to the original one is lost.
So it is NOT the instance of the NSString you created with [[NSString alloc] initWithString:#""]
The NSString returned from stringByAppendingFormat is autoreleased and your [locStr release] would over-release it. As the other answers indicate you could just use a NSMutableString to avoid lots of NSString instances to be created in your loop and actually releasing the original created instance.
No need to alloc/init locStr. This should do the trick:
-(void)appendString{
NSString *locStr = #"";
for (int i=0;i<[result count]; i++) {
locStr=[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]];
}
[str setString:locStr];
}
Don't release, use autorelease. This is the way if you need to return something. When there is an autorelease pool (which is in most cases) it is released automatically. That's what methods like stringByAppendingFormat do also.
Or wait, when you di something like this, it is important, that you retain the Object in the setter Method. It is better then to use a property like
{
NSString* thestring;
}
#property (nonatomic, retain) NSString* thestring;
and a
#synthesize thestring;
in the .m file.
use this
locStr=[[locStr stringByAppendingFormat:#"%#",[result objectAtIndex:i]] retain];
then you can release this same as you are doing.
what happens here you get an autorelease object of string by stringByAppendingFormat method.
so you need to convert it into a retain copy.
Related
I want to be sure that my code is not leaking, since this small snippet is called thousand times in my app. I run the app through Instruments and the initWithBytes seems to be problematic. Is anything wrong in this code?
First [reader readString] is called.
case FirstCase:
{
NSString *string = [reader readString];
[self setPropertyByName:propertyName value:string];
break;
}
...
readString is returns the strings which is autoreleased.
- (NSString*) readString
{
...
NSString *string = [[[[NSString alloc] initWithBytes:cursor length:stringLength encoding:NSUTF8StringEncoding] autorelease];
return string;
}
Is the code OK? Any other better approach to avoid autorelease?
I cannot change my code to ARC. Plain old non-ARC memory management.
What you posted is OK. The only rule at this point is that methods contain "create" or "alloc" will return an object that needs to be explicitly released. In your case that is the string returned in the readString method.
Since the object will be returned you need to retain it till the end of the run loop cycle which the autorelease pool will do. What that means for instance is if this method will be called in a for loop the objects will not be deallocated before the loop has exited.
If you want or need to avoid that I suggest you to do the same pattern with "create" or "alloc" and return an object not being autoreleased:
case FirstCase:
{
NSString *string = [reader createReadString];
[self setPropertyByName:propertyName value:string];
[string release];
break;
}
...
- (NSString*) createReadString
{
...
NSString *string = [[[NSString alloc] initWithBytes:cursor length:stringLength encoding:NSUTF8StringEncoding];
return string;
}
In NSString NSString Class Reference what this means
Distributed objects:
Over distributed-object connections, mutable string objects are passed by-reference and immutable string objects are passed by-copy.
And NSString can't be changed, so what happening when I am changing str in this code
NSString *str = #"";
for (int i=0; i<1000; i++) {
str = [str stringByAppendingFormat:#"%d", i];
}
will I get memory leak? Or what?
What your code is doing:
NSString *str = #""; // pointer str points at memory address 123 for example
for (int i=0; i<1000; i++) {
// now you don't change the value to which the pointer str points
// instead you create a new string located at address, lets say, 900 and let the pointer str know to point at address 900 instead of 123
str = [str stringByAppendingFormat:#"%d", i]; // this method creates a new string and returns a pointer to the new string!
// you can't do this because str is immutable
// [str appendString:#"mmmm"];
}
Mutable means you can change the NSString. For example with appendString.
pass by copy means that you get a copy of NSString and you can do whatever you want; it does not change the original NSString
- (void)magic:(NSString *)string
{
string = #"LOL";
NSLog(#"%#", string);
}
// somewhere in your code
NSString *s = #"Hello";
NSLog(#"%#", s); // prints hello
[self magic:s]; // prints LOL
NSLog(#"%#", s); // prints hello not lol
But imagine you get a mutable NSString.
- (void)magic2:(NSMutableString *)string
{
[string appendString:#".COM"];
}
// somewhere in your code
NSString *s = #"Hello";
NSMutableString *m = [s mutableCopy];
NSLog(#"%#", m); // prints hello
[self magic2:m];
NSLog(#"%#", m); // prints hello.COM
Because you pass a reference you can actually change the "value" of your string object since you are working with the original version and not a duplicate.
NOTE
String literals live as long as your app lives. In your exmaple it means that your NSString *str = #""; never gets deallocated. So in the end after you have looped through your for loop there are two string objects living in your memory. Its #"" which you cannot access anymore since you have no pointer to it but it is still there! And your new string str=123456....1000; But this is not a memory leak.
more information
No, you will not get memory leak with your code, as you are not retaining those objects in the loop, they're created with convenience method, you don't own them, and they will be released on next cycle of autorelease pool. And, it's doesn't matter if you are using ARC or not, objects created with convenience methods and not retained are released wherever they are out of their context.
In will not leak memory, but will get more memory allocation, due to making new copy of immutable copy as many time loop triggers [str stringByAppendingFormat:#"%d", i];.
Memory leak will get performed when, you put your data unreferenced, or orphan, this will not make your last copy of string orphan every time when loops, but will clear all copies of NSString when operation get complete, or viewDidUnload.
You will not get a memory leak in the example code because Automatic Reference Counting will detect the assignment to str and (automatically) release the old str.
But it would be much better coding style (and almost certainly better performance) to do this:
NSMutableString* mstr = [NSMutableString new];
for(int i = 0; i < 1000; ++i){
[mstr appendFormat:#"%d",i];
}
NSString* str = mstr;
...
As to the first question, I think it means that a change made to a mutable string by a remote process will be reflected in the originating process's object.
Getting EXC_BAD_ACCESS when setting a label with the value returned by the following function defined in a class called DataHelper (all database handling done here):
+(NSString *)getCurrentBalanceAndType:(NSString *)account
{
sqlite3_stmt *statement=NULL;
float bal=0;
NSString *type=NULL, *balance_type=NULL;
//String decimalFormat="%.2f";
statement = [DataHelper getDetailsFromAccountBal:account:[DataHelper currentMonth]];
if (sqlite3_step(statement) == SQLITE_ROW)
{
bal = sqlite3_column_double(statement,2);
type = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
}
sqlite3_finalize(statement);
NSString *stringBal=[NSString stringWithFormat:#"%.2f", bal];
type=[[type uppercaseString] stringByAppendingString:#"r"];
balance_type=[[stringBal stringByAppendingString:#" "]stringByAppendingString:type];
[type release];
return balance_type;
}
This is how I am setting the label in viewDidLoad:
lbCreditCurrBal.text=[DataHelper getCurrentBalanceAndType:#"Some Text"];
Please help.
You are overreleasing type. Remove [type release];. And use ARC to avoid this kind of errors.
When you call a method that begins with anything except init or copy, you’re returned an object that will be autoreleased at some point in the future.
reference- ray's memory management blog
keeping above in mind I think you should not release the type object as said by Nikolai.
I've created autorelease pool. localString has added to this pool. I released the pool. localString and string must be deallocated. But in reality they are still alive. You can see my log:
Why is the string object still alive? I don't know.
and code:
-(NSString*) happyString
{
NSString *localString = [[[NSString alloc] initWithString:#"I don't know."] autorelease];
return localString;
}
-(IBAction) onButton:(id)sender
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *string = [self happyString];
[pool release];
NSLog(#"Why is the string object still alive? %#", string);
}
Strings (NSString instances and statically allocated strings with #"") are immutable in Cocoa, so when you try to create a new NSString from a statically allocated one, the NSString class can make an optimisation: a new NSString instance is not created (the object created when you called -alloc is immediately released), and the reference to your statically allocated string is returned. That is, the line:
NSString *localString = [[[NSString alloc] initWithString:#"I don't know."] autorelease];
Is actually equivalent to:
NSString *localString = #"I don't know.";
(If you check the memory addresses of those two objects, you can see that they are the same.)
As this type of string cannot be released, it does not disappear when you expect it to.
If you were to create your string in a way that cannot be optimised, for example:
NSString *localString = [[[NSString alloc] initWithFormat:#"%#", #"I don't know."] autorelease];
Then your code will behave as you expect, and your application will (hopefully) crash at your NSLog line.
If you have tried any classes (any custom classes) other than NSString , then it would not be alive..
I have NSMutableArray* cityData that I fill with custom LocationDetail objects. cityData is created in viewDidLoad and released in dealloc.
Somewhere in the code, based on user actions, I populate LocationDetail and add it to cityData array:
LocationDetail* d = [[LocationDetail alloc] init];
d.city = [NSString stringWithFormat:#"%S", (char*)sqlite3_column_text16(statement, 1)];
d.tz = [NSString stringWithFormat:#"%S", (char*)sqlite3_column_text16(statement, 3)];
d.country = [NSString stringWithFormat:#"%S", (char*)sqlite3_column_text16(statement, 2)];
d._id = [NSString stringWithFormat:#"%S", (char*)sqlite3_column_text16(statement, 0)];
[cityData addObject:d];
[d release];
When I am finished with the view controller and remove it, Leaks utility says I have a leak in the code above in NSCFString in all 4 lines with [NSString stringWithFormat] above.
I tried removing the sqlite3 stuff and simplified the call to something like
d._id = [NSString stringWithFormat:#"%s", "a string"]
with the same result. However, if I replace the NSString stringWithFormat like this:
d._id = #"a string";
the leak goes away. I wonder why there is a leak if I use the stringWithFormat, but not if I use #"something". Is there anything obvious that I'm doing wrong?
Thanks!
Properties are not automatically released for you, you need to do that yourself in
- (void)dealloc
See The Objective-C Programming Language: Declared Properties for an example.
Edit:
It seems that the example was moved into the Advanced Memory Management Programming Guide.