Even after invalidate a NSURLSession, running a profile using Instruments, some classes (probably privates) called TubeManager, HTTPConnectionCache and HTTPConnectionCacheDictionary still alive in memory.
Code snippet to reproduce:
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
NSURLSessionDataTask* sessionDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
[session finishTasksAndInvalidate];
}];
[sessionDataTask resume];
finishTasksAndInvalidate called in wrong place... completionHandler is for handling response it has nothing to do with session
Here is correct code:
So, what is the question? Do you want to turn off caching? Network responses are routinely cached in
NSURLCache
in both memory and persistent storage.If this cache usage is problematic, change the
requestCachePolicy
for the session configuration accordingly. Or change thecachePolicy
of aNSMutableURLRequest
, itself. You can also configure the maximum size of theURLCache
that the session configuration uses, to constrain both the amount of RAM usage as well as the amount of persistent memory usage.Even if you turn off caching, as a general rule one should not be surprised that API calls increase memory consumption, through no fault of your own. It's not uncommon for an app to experience some modest memory consumption the first time it performs some task. But one should not see the same growth in subsequent iterations while the app runs. When tracking memory usage, it's generally advisable to repeat the task multiple times and see if the app returns back to some steady state (which is desirable), or whether it continues to grow (which requires further investigation to make sure your code is not the source of the problem). But we rarely worry about the initial memory consumption unless it is dramatic.
Looking at your code snippet, there's nothing obviously wrong. I'd be inclined to suspect routine memory consumption by iOS. Assuming the issue is broader than the general cache behavior, if the memory consumption is dramatic and/or continues to grow each time the app performs this code, then provide more details and we can help you diagnose this further.
This is what my memory profile looks like after four batches of 100 requests each; plus a final flag after I issued a memory warning:
(Note, that's a composite image so I could show you memory before the first batch, before the third batch, after the last batch and after I manually posted the memory warning. I combined these together to make it easier to see what the total allocations were at those four points in time.)
Note that on iOS 9, Security framework allocates appx. 4k of SSL cache data and charges it to your app the first time you resume a task for a new NSURLSession object. Apple Technical Q&A QA1727 tells us this SSL cache persists for 10 minutes, no matter what, since it's private and entirely managed by the system (because security!).
In your code example, you are creating a new NSURLSession object each time you make a request. But you are just using defaultSessionConfiguration and not specifying a delegate to which a strong reference might exist, then what you should instead do is just use the singleton
[NSURLSession sharedSession]
and useresetWithCompletionHandler
to clear up non-security allocations. Or make a custom singleton if you want to customize the configuration.(From NSURLSession Class Reference... Italics mine ;P)
If you are not using the
sharedSession
Apple-provided singleton, then you should at least take a cue from Apple and roll your own singleton with a session property. The point of a session is that it is intended to live longer than just one request. Despite their unclear documentation, the fact that Apple provides a singleton, and calls it a "session", indicates session objects were meant to live longer than just a single request.Yeah, you are supposed to
invalidateAndCancel
at some point, but not after every single request, and not even if each request is going to a different server entirely (which is almost never the case). You only need to invalidate and cancel if you are going to break your reference to a particular session; otherwise, you can just callflushWithCompletionHandler
orresetWithCompletionHandler
on the session to flush your session's heap allocations to VM, or reset to clear both heap and VM storage. (Also see my answer here.)