90

On iPhone, I perform a HTTP request using NSURLRequest for a chunk of data. Object allocation spikes and I assign the data accordingly. When I finish with the data, I free it up accordingly - however instruments doesn't show any data to have been freed!

My theory is that by default HTTP requests are cached, however - I don't want my iPhone app to cache this data.

Is there a way to clear this cache after a request or prevent any data from being cached in the first place?

I've tried using all the cache policies documented a little like below:

NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
theRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

but nothing seems to free up the memory!

nhgrif
  • 58,130
  • 23
  • 123
  • 163
Nick Cartwright
  • 8,194
  • 14
  • 42
  • 56
  • Would it be possible it's not cache related? Have you tried inspecting the data to see if data that should have been reloaded is in fact the old one? Maybe your leek is coming from elsewhere. How do you initialize and free the NSURLRequests? That might be of help to diagnose the problem. – lpfavreau Jan 01 '09 at 17:16
  • FYI – If you want to remove the files by brute force, there is example code to do that here: http://salesforce.stackexchange.com/a/69736 – zekel Feb 01 '16 at 18:57

6 Answers6

158

Usually it's easier to create the request like this

NSURLRequest *request = [NSURLRequest requestWithURL:url
      cachePolicy:NSURLRequestReloadIgnoringCacheData
      timeoutInterval:60.0];

Then create the connection

NSURLConnection *conn = [NSURLConnection connectionWithRequest:request
       delegate:self];

and implement the connection:willCacheResponse: method on the delegate. Just returning nil should do it.

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
  return nil;
}
tcurdt
  • 10,884
  • 9
  • 55
  • 65
  • Thanks so much here for your help! Nick. – Nick Cartwright Jan 02 '09 at 09:36
  • 1
    Thanks for that idea - I was calling a SOAP web service like this repeatedly and it was growing the heap uncontrollably even though leaks didn't show anything was wrong. I optimized for days and finally tried to prevent caching since a lot of CFURL* objects from the internal framework were hanging around. Returning nil from willCacheResponse was the only thing that worked! – Bron Davies Sep 30 '10 at 19:37
  • this is really helpful in case of background apps when they are trying to acces same url :) thax! – Adeem Maqsood Basraa Mar 02 '11 at 23:41
  • 15
    Why is it necessary to do both `NSURLRequestReloadIgnoringCacheData` *and* implement `connection:willCacheResponse:`? – fabb Sep 01 '11 at 11:07
  • 1
    Hi, is it possible that I can use this for loading local content? The above is for NSUrlConnection but i'm loading local HTML data into UIWebView using NSUrlRequest. I need to reject any caching as there are images going into the webview from SQLite and memory is increasing with every page load. Thanks. – jim Oct 06 '11 at 10:24
  • 7
    @fabb, overriding `connection:willCacheResponse:` allows you to not store the response in the cache. `NSURLRequestReloadIgnoringCacheData` specifies that the connection should load the request without checking the cache. The former is presumably what helps manage memory allocation. – Christopher Pickslay Nov 15 '11 at 22:09
  • Curiously, `connection:willCacheResponse:` is not documented as a method of NSURLConnectionDelegate on the Apple website. – Hot Licks May 23 '12 at 22:18
  • @HotLicks It is, indeed a required method [Return Value The actual cached response to store in the cache. The delegate may return cachedResponse unmodified, return a modified cached response, or return nil if no cached response should be stored for the connection.](https://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSURLConnectionDelegate_Protocol/Reference/Reference.html#jumpTo_10) – bobobobo May 08 '13 at 16:32
  • @bobobobo -- Ah, it's long deprecated in iOS. Didn't look at OSx. – Hot Licks May 08 '13 at 16:57
  • @HotLicks -- It's availiable in iOS 5.0 and later as part of the NSURLConnectionDataDelegate. https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html#//apple_ref/occ/intf/NSURLConnectionDataDelegate – Richard Brightwell Jan 09 '14 at 15:18
  • Pardon a n00b, but how exactly does one read the response data from the web page with this approach? When using the sendSynchronousRequest method you get NSData back, but with the approach here you get a connection object back. How do you get the response HTML/JSON/data from the connection object? – Lev Jun 16 '15 at 07:26
  • @Lev that's not the right place for questions like that - but read up on delegates. – tcurdt Jun 16 '15 at 10:49
  • @tcurdt I was aware of the delegate already, just not which methods or approach to use. ;P In any event, I stumbled upon a tutorial yesterday which helped me figure it out. – Lev Jun 17 '15 at 14:05
12

I have the same problem in my app when I requested info from twitter. In my case I didn't need to preserve those credentials, so I simple erase them using the next code:

- (void) eraseCredentials{
NSURLCredentialStorage *credentialsStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *allCredentials = [credentialsStorage allCredentials];

//iterate through all credentials to find the twitter host
for (NSURLProtectionSpace *protectionSpace in allCredentials)
    if ([[protectionSpace host] isEqualToString:@"twitter.com"]){
        //to get the twitter's credentials
        NSDictionary *credentials = [credentialsStorage credentialsForProtectionSpace:protectionSpace];
        //iterate through twitter's credentials, and erase them all
        for (NSString *credentialKey in credentials)
            [credentialsStorage removeCredential:[credentials objectForKey:credentialKey] forProtectionSpace:protectionSpace];
    }
}

I hope it works for somebody :)

  • this was a perfect solution for my problem, i had relogin problem, since the credentials were stored and were automatically being submitted by the NSURLConnection, thanks a lot this helped me big time :) – RVN Apr 07 '11 at 10:57
  • Thanks it's work for me, i am really appreciate your suggestion. Actually My problem is that NSURLRequest storing username and password. So this help to remove User credential from cache... – Nilesh Kikani Oct 09 '12 at 14:49
10

If you're using NSURLSession, another solution to prevent request and parameters being written to the Cache.db iOS creates within the app's Caches directory, is to set the NSURLCache for the session's configuration to a 0 size memory and 0 size disk cache e.g.

let configuration = URLSessionConfiguration.default    
configuration.urlCache = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
let session = URLSession(configuration: configuration)

or as mentioned above set at a global cache level

URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)

Presumably it's the 0 for disk size that stops iOS writing to disk but if you have a policy to reloadIgnoringLocalCacheData then you probably aren't interested in memory caching either.

Note This will prevent any Caches/Cache.db (requests & responses) or Caches/fsCachedData/ folder (response data) being created at all. We've decided to take this approach in an app for security purposes as we don't want our requests to be stored on disk cache ever.

If anyone knows is there's a way to stop only request caching but keep response data caching from the iOS URL Loading mechanism, I'd be interested to know. (there's no API or official documentation about this from what I can tell)

10

If you use NSURLConnection take a look at the delegate:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse

Return Value

The actual cached response to store in the cache. The delegate may return cachedResponse unmodified, return a modified cached response, or return nil if no cached response should be stored for the connection.

catlan
  • 23,677
  • 8
  • 64
  • 74
7

If not specific to a single request(U want disable cache for whole app) below one is the best option.Add this code in app delegate or based on ur need any where

        int cacheSizeMemory = 0; // 0MB
        int cacheSizeDisk = 0; // 0MB
        NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
        [NSURLCache setSharedURLCache:sharedCache];
Evgen Bodunov
  • 3,778
  • 1
  • 26
  • 38
Sanjeev Rao
  • 1,977
  • 1
  • 16
  • 18
  • 7
    Works great for me, but you can just use 0 rather than doing the multiplication that just ends up at 0 anyways. – Gary Riches Nov 11 '16 at 11:28
2
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] url];
[request setValue:@"no-store" forHTTPHeaderField:@"Cache-Control"];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

Assuming the server is correctly implemented, putting the Cache-Control:no-store header in the request will generate a server response with the same header, thus causing NSURLCache to not store the response data on disk.

Therefore, no need for the shotgun approach of disabling NSURLCache disk caching.

PS: Adding the header should work for all HTTP frameworks, like AFNetworking

Yuri Brigance
  • 487
  • 4
  • 12