2

I have several memory leaks(NO! see Update 1) in my App and they all come down to an asynchronous URLRequest. The code below gives me a memory leak, it seems like the 'data' is never released (The code below is logically not used in my app as it is a completely useless infinite loop, I just wrote it to show the memory leak. This makes the used RAM go from 5 to 20 MB in less than one second. My internet speed does match this [just for the record]):

- (void)start{
    NSOperationQueue *oQC = [[NSOperationQueue alloc] init];
    NSLog(@"a");
    [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.online-image-editor.com//styles/2014/images/example_image.png"]] queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSLog(@"b");
        [self start];
    }];
}

I have also tried setting data = nil, but that did, like supposed, not work. Does anybody have any Idea of how to avoid the memory leak and if this is the normal behavior of a NSURLConnection?

UPDATE 1:

This doesn't seem to be related to a memory leak but to a caching problem. (Thanks to @rdelmar, who saw the problem, but his solution doesn't quite work)

Based on this post I have tried creating a new 'Loader' class with this in it's .h file:

#import <Foundation/Foundation.h>

@interface Loader : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>

@property NSMutableData *mData;
@property (nonatomic, copy) void (^returnBlock)(NSData *data, NSError *error);

- (id)initForWhateverWithURLString:(NSString*)urlString andHandler:(void (^)(NSData *data, NSError *error))handler;

@end

And this in it's .m file:

#import "Loader.h"

@implementation Loader

@synthesize mData;

- (id)initForWhateverWithURLString:(NSString*)urlString andHandler:(void (^)(NSData *data, NSError *error))handler{
    self = [super init];
    if (self) {
        /*NSOperationQueue *oQC = [NSOperationQueue mainQueue];
        [NSURLConnection sendAsynchronousRequest:mUR queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
            [[NSURLCache sharedURLCache] removeCachedResponseForRequest:mUR];
            handler(nil, nil);
         }];*/
        mData = [NSMutableData new];
        self.returnBlock = handler;
        NSMutableURLRequest *mUR = [[NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60] mutableCopy];
        NSURLConnection *URLCon = [[NSURLConnection alloc] initWithRequest:mUR delegate:self];
        [URLCon start];
    }
    return self;
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse{
    return nil;
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    [mData appendData:data];
    data = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    self.returnBlock(mData, nil);
    mData = nil;
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    self.returnBlock(nil, error);
}

@end

I also implemented:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:@"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];

Still, neither of these methods helps reducing the drastic RAM usage!

Community
  • 1
  • 1
user2456014
  • 345
  • 1
  • 4
  • 13
  • Can you file a bug on radar (https://bugreport.apple.com)? I've filed a bug report (19871483), it looks like iOS 8 is leaking with NSURLConnection. We also see this problem in our apps. – user1687195 Feb 18 '15 at 06:09
  • I have done this. Thanks for the feedback. – user2456014 Mar 19 '15 at 11:47

2 Answers2

3

I don't think what you're seeing is a leak. I think the memory keeps increasing because the data is being cached. If you change the caching protocol of the request, the problem goes away (you're getting the default behavior, NSURLRequestUseProtocolCachePolicy, by using requestWithURL:)

- (void)viewDidLoad {
    [super viewDidLoad];
    self.req = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.online-image-editor.com//styles/2014/images/example_image.png"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
    [self start];
}

- (void)start{
    NSOperationQueue *oQC = [NSOperationQueue mainQueue]; // this queue is where the completion block runs, so you should use mainQueue if you want to do any UI updating
    NSLog(@"a");
    [NSURLConnection sendAsynchronousRequest:self.req queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSLog(@"b");
        [self start];
    }];
}

After Edit:

After looking at this some more, I'm not sure why sendAsynchronousRequest: causes a rise in memory usage with time. I tested using NSURLSession (which is what we should be using now anyway), and that seemed to work without causing a memory increase.

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *urlString = @"http://www.online-image-editor.com//styles/2014/images/example_image.png";
    self.req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
    [self start];
}

- (void)start{
    NSLog(@"a");
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:self.req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        self.counter ++;
        NSLog(@"counter is: %ld", self.counter);
        [self start];
    }];
    [task resume];
}
rdelmar
  • 102,832
  • 11
  • 203
  • 218
  • This seems true, but I have to say the problem is still not 'solved' it is rather 'drastically minimized'. The RAM usage still increases, whereas this increment is far less drastic than it was before. – user2456014 Dec 30 '14 at 11:11
  • OK after trying around with this for some time and searching the web some more I found that this does not help. Look at my update in the main post for more information. – user2456014 Dec 30 '14 at 16:37
  • @user2456014, I've updated my answer with a method that I think works better. I didn't see any increase in memory usage after a couple of hundred downloads. – rdelmar Dec 30 '14 at 18:40
  • Yes this is better and gives me 0 MB RAM increase after several requests, too. Thanks. – user2456014 Dec 30 '14 at 21:06
0

if you are not using ARC then change

[[NSOperationQueue alloc] init];

to

[[[NSOperationQueue alloc] init] autorelease];

and

[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.online-image-editor.com//styles/2014/images/example_image.png"]]

to

[[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.online-image-editor.com//styles/2014/images/example_image.png"]] autorelease]
hariszaman
  • 7,457
  • 2
  • 35
  • 53