views:

234

answers:

1

My application seems to have 4 memory leaks (on the device, running instruments).

The memory leaks seems to come from this code:

NSURL *url = [self getUrl:destination];
[destination release];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

[request setHTTPMethod:@"GET"];
[request addValue:@"application/json" forHTTPHeaderField:@"content-type"];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
[request release];

[connection release];

EDIT: added code for getUrl

- (NSURL *)getUrl:(NSString *)actionUrl
{
    NSString *rawUri = [[NSString alloc]initWithFormat:@"%@/%@", kBaseUrl, actionUrl];
    NSURL *url = [[[NSURL alloc] initWithString:rawUri] autorelease];
    [rawUri release];
    return url;
}

I am releasing all my objects as far as I can see but it's still showing this as the source of the 4 memory leaks.

This is on the Device running 3.1.3

Is it acceptable to have a few memory leaks in your app or do they all have to go?

EDIT: I've added autorelease to getUrl. However it still shows up with memory leaks

EDIT2: The behaviour is rather strange. I launch the app and hit the button that makes this call once. 4 leaks are discovered. I press back and hit the button again, and keep doing this a few times, and still only 4 leaks. However, if I wait a few seconds and then press the button a gain a few more times, 9 leaks are discovered. It's not a small 128 byte leak, but it's 1.61KB at this point.

EDIT3: Here is the connectionDidFinishLoading

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    SBJSON *jsonParser = [[SBJSON alloc] init];
    NSString *jsonString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
    [receivedData setLength:0];
    [receivedData release];
    [self.delegate dataReceived:[jsonParser objectWithString:jsonString]]; // See method below
    [jsonParser release];
    [jsonString release];
}

The delegate gets the data, then transforms it (and in return passes it on to another delegate once the product is constructed)

- (void)dataReceived:(id)data
{
    NSMutableArray *myObjects = [[NSMutableArray alloc]init];
    ObjectFactory *objectFactory = [[ObjectFactory alloc]init];

    // Only one object
    if ([data isKindOfClass:[NSDictionary class]])
    {
        Object *object = [objectFactory buildObject:data];
        [myObjects addObject:object];
        [object release];
    }

    // Multiple objects
    if ([data isKindOfClass:[NSArray class]])
    {
        for (NSDictionary *objectSrc in data)
        {
            Object *object = [objectFactory buildObject:post];
            [myObjects addObject:object];
            [object release];
        }
    }
    [objectFactory release];
    [self.delegate objectsReceived:myObjects];
}

EDIT4: Something I did notice is that the object "ConnectionObject" that contains the NSUrlConnection, never seem to be deallocated.

I put a breakpoint on dealloc which calls [connection release] This dealloc is never called. All the deallocs are called down the chain except for this one. I tried [connection cancel] in the "connectionDidFinishLoading" call to see if that helped but not at all.

This sure is a mystery to me...

A: 

You are releasing something you shouldn't:

NSURL *url = [self getUrl:destination]; 
// the returned url should have been autoreleased by the getUrl: method
// so you shouldn't release it again

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

[url release]; // don't do this!

Remember that you should only release objects that were created using alloc, new or retain . Objects returned from other methods are always in an auoreleased state (by convention).

Philippe Leybaert
I thought this, though it occurred to me that it's possibly the fault of -getUrl which allocates an NSURL and doesn't release, meaning the call to [url release]; will actually result in a reference count of 0, which is what you want! If this is the case, then you'll need to autorelease in -getUrl then retain its return NSURL when you call it.
David Foster
A method that allocates a (temporary) object should **never** return that object without calling autorelease on it first. **NEVER**.
Philippe Leybaert
"David: no it isn't what you want. What you want is to return an object that the caller doesn't have to release because that is what the caller is expecting. Or you can change the method name from getUrl: to newURL:. By the way, forget about retain counts. Think only about ownership. Retain counts are often misleading. For example if you autoreleased the URL in getURL: the retain count will still be at least 1 on return.
JeremyP
Jeremy, I'm well aware that one can't at all rely on reference counts as a means of memory management. I too recommend one only ever consider the principle of object ownership (which when adhered to prevents the majority of memory leaks often encountered) but I make mention of reference counts solely to quantify the number of allocs, news, retains and releases.
David Foster