Davis
Davis

Reputation: 1263

NSURLConnection Memory Leak

I run the instruments tool and get some Memory Leaks and i don´t know how to handle this. I´m using ARC!

This is my Code:

+ (MARequest *)requestImageThumb:(NSString *)imageName
                      object:(NSInteger)objectId {
   NSString* urlString = [NSString stringWithFormat:@"%@/%@", kBaseImageThumbURL, imageName];

   LogTrace(@"Creating image thumb request for file %@", imageName);

   //Here starts the leak!!
   return [MARequest createWithURL:[NSURL URLWithString:urlString]
                           type:REQUEST_TYPE_GET_IMAGE];
}


+ (MARequest *)createWithURL:(NSURL *)url
                    type:(NSInteger)type {

   MARequest* r = [[MARequest alloc] init];

   r.url = url;
   r.requestType = type;
   r.responseData = [[NSMutableData alloc] init];
   r.connection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:r.url]
                                               delegate:r
                                       startImmediately:NO];

   return r;
}

And here my NSURLConnectionDelegate

#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
   LogTrace(@"request %@: didReceiveResponse", self.url);
   [self.responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
   LogTrace(@"request %@: didReceiveData, %d bytes", self.url, data.length);

   [self.responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
   LogTrace(@"request %@: didFailWithError: %@", self.url, [error description]);

   self.connection = nil;
   self.failed = YES;

[self invokeAction];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
   LogTrace(@"request %@: connectionDidFinishLoading, %d bytes", self.url, [self.responseData length]);
   self.connection = nil;

   [self invokeAction];
}

EDIT:

I changed my Code to this now, but sadly it still gives me a Memory Leak....

+ (MARequest *)requestImageThumb:(NSString *)imageName
                      object:(NSInteger)objectId {
    NSString* urlString = [NSString stringWithFormat:@"%@/%@", kBaseImageThumbURL, imageName];

    LogTrace(@"Creating image thumb request for file %@", imageName);

    return [MARequest requestWithURL:[NSURL URLWithString:urlString]
                           type:REQUEST_TYPE_GET_IMAGE];
}

+ (MARequest *)requestWithURL:(NSURL *)url
                    type:(NSInteger)type {

   MARequest* r = [[MARequest alloc] init];

   r.url = url;
   r.requestType = type;
   r.responseData = [[NSMutableData alloc] init];
   r.connection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:r.url]
                                               delegate:r
                                       startImmediately:NO];



   return r;
}

Upvotes: 3

Views: 1917

Answers (2)

sergio
sergio

Reputation: 69027

EDIT: after your changes, the explanation for the leak is the following:

You have a circular dependency between your GFRequest object and the NSURLConnection object, thus preventing both from being correctly deallocated. Indeed, you are setting the GFRequest's connection property to a NSURLConnection instance:

   r.connection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:r.url]

while at the same time also making the r be the delegate of that NSURLConnection:

             delegate:r

What happens is that NSURLConnection's delegate does a retain on its delegate, and this is causing the dependency cycle.

I do not see an easy way to break the dependency, though, and keep your current design.

You could simply avoid storing your connection inside of the request, or you could not use the request as a delegate for the connection. Possibly, you should consider the possibility of subclassing NSURLConnection and have it act as a delegate to itself.

OLD ANSWER:

There is a mismatch between the outer method name and the inner one:

+ (MARequest *)requestImageThumb:(NSString *)imageName
…
   return [MARequest createWithURL:[NSURL URLWithString:urlString]

For the static analyser those have different semantics as to object ownership.

Replace the first one with:

+ (MARequest *)createRequestImageThumb:(NSString *)imageName

or the second one with:

   return [MARequest requestWithURL:[NSURL URLWithString:urlString]

Depending on which semantics is proper for you case.

Upvotes: 2

colinbrash
colinbrash

Reputation: 481

Sounds like what is going on is that you have started the method with the word create which indicates to ARC that it will return an object with a retain count of 1. In other words, create indicates that the caller is going to be responsible for releasing the object when it is done.

But then you return this object directly inside another method that does not start with create. And this indicates that if the caller of this second method should be retaining the object if it wants to keep it around.

There is a conflict here and I think ARC does not know what to do. Should it be releasing the object or not?

If you rename +createWithURL:type: to +requestWithURL:type: this should fix it because +requestWithURL:type: will return an autoreleased object, which is what +requestImageThumb:object: is expecting to return.

Alternatively, rename +requestImageThumb:object: to +createRequestWithImageThumb:object: to explicitly ask +createRequestWithImageThumb:object: to return a retained object, which it is getting from +createWithURL:type:.

Upvotes: 1

Related Questions