views:

36

answers:

3

I have a program that reads a huge text file (line by line) and does some string operations with each line before writing the line into a database.

The program needed more and more memory so I figured that I might need to release the strings that I use. But it did not help. So I have put together the following code to test out what actually happens. With some trial and error I found out that when I do the drain on the autorelease pool it works.

I would like to know what I do. So I ask:

  • Why is the release not releasing memory?
  • Is there a better way to do this?

Here is my test program

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

int cnt = 0;

while (cnt < 1000000000) {
    NSMutableString *teststr = [[NSMutableString alloc] init];

    teststr = [NSString stringWithString:@"Dummy string just for the demo"];

    cnt++;
    if (cnt % 1000000 == 0) {
        printf("cnt=%i\n",cnt);
    }

    [teststr release];


//      [pool drain];                      // It works when I do this 
//      [[NSAutoreleasePool alloc] init];  // and this

}

[pool drain];
return 0;
}

EDIT: Based on the answers so far I have looked on my original program and changed the test program:

//teststr = [NSString stringWithString:@"Dummy string just for the demo"];

[teststr appendString:@"Dummy string just for the demo"];

Does this also create a new string? Because still I have the memory problem. My routine works in way that I append the string with something but maybe start with an empty string at the beginning.

+1  A: 

You are making one very basic mistake.

  1. You have to release an object when you call alloc/init on it.
  2. An object is auto-released if you get an object using other means (convient constructors, returned objects of methods etc).

The method stringWithString returns a new autoreleased string so there is no point for to alloc/init it. Also since it is auto-released object, draining the auto-released pool helps.

So instead of:

NSMutableString *teststr = [[NSMutableString alloc] init];
teststr = [NSString stringWithString:@"Dummy string just for the demo"];

Try this:

NSMutableString *teststr = [NSString stringWithString:@"Dummy string just for the demo"];
Hemant
+1  A: 
NSMutableString *teststr = [[NSMutableString alloc] init];

This allocates a mutable string....

teststr = [NSString stringWithString:@"Dummy string just for the demo"];

and then this overrides the teststr variable with an autoreleased string. The allocated mutable string is now inaccessible, but still having a retain count of +1, so it will be leaked.

[teststr release];

This will only release the autoreleased string, causing double-free error in the future.


If you want a manually-managed mutable string, you should use

NSMutableString* teststr = [[NSMutableString alloc] initWithString:@"Dummy string just for the demo"];
...
[teststr release];

and don't assign to teststr directly, before it is released or the ownership is transferred.

KennyTM
I tried it and it works. When I initialize as you suggested I have no memory problem. I start with an empty string so I did it like this: NSMutableString *teststr = [[NSMutableString alloc] initWithString:@""]; but I wonder why this works better then just "init". I thought that "init" does the same (giving me an empty string). Anyway thanks for the answer.
Jürgen Hollfelder
@Jürgen: Yes `-init` gives an empty string. This doesn't always work better, but since you start with a known string, I would combine the init and append together as `-initWithString:`.
KennyTM
Yes I tried around a little bit. The basic problem was really in my original program that I initialized but then created a new string and the release only released the last one.
Jürgen Hollfelder
A: 

The above example was not really having so much the memory problem. My more complicated program "leaked" memory and this was because of the following statement which consumed up memory without a drain.

NSString *conv = [dict objectForKey:astring]];

It is not really a leak but several statements of that kind and an iteration of several hundred thousand caused a big issue. The solution was to drain the autorelease pool. But draining the autorelease pool had the disadvantage that the dictionary object (dict) which I use was drained, too.

So this handled it. I opened a second pool:

NSAutoreleasePool * pool2 = [[NSAutoreleasePool alloc] init];

NSString *conv = [dict objectForKey:astring]];

/* do something */

[pool2 drain];
Jürgen Hollfelder