NSURLSession memory leak

孤人 提交于 2019-12-01 11:10:29

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 the cachePolicy of a NSMutableURLRequest, itself. You can also configure the maximum size of the URLCache 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.)

CommaToast

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 use resetWithCompletionHandler to clear up non-security allocations. Or make a custom singleton if you want to customize the configuration.

  • (NSURLSession *)sharedSession Discussion

For basic requests, the URL session class provides a shared singleton session object that gives you a reasonable default behavior. By using the shared session, you can fetch the contents of a URL to memory with just a few lines of code.

Unlike the other session types, you do not create the shared session; you merely request it by calling [NSURLSession sharedSession]. As a result, you don’t provide a delegate or a configuration object. Therefore, with the shared session:

   - You cannot obtain data incrementally as it arrives from the server.
   - You cannot significantly customize the default connection behavior.
   - Your ability to perform authentication is limited.
   - You cannot perform background downloads or uploads while your app is    
     not running.

The shared session uses the shared NSURLCache, NSHTTPCookieStorage, and NSURLCredentialStorage objects, uses a shared custom networking protocol list (configured with registerClass: and unregisterClass:), and is based on a default configuration.

When working with a shared session, you should generally avoid customizing the cache, cookie storage, or credential storage (unless you are already doing so with NSURLConnection), because there’s a very good chance that you’ll eventually outgrow the capabilities of the default session, at which point you’ll have to rewrite all of those customizations in a manner that works with your custom URL sessions.

In other words, if you’re doing anything with caches, cookies, authentication, or custom networking protocols, you should probably be using a default session instead of the default session.

(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 call flushWithCompletionHandler or resetWithCompletionHandler 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.)

finishTasksAndInvalidate called in wrong place... completionHandler is for handling response it has nothing to do with session

Here is correct code:

NSURLSessionConfiguration* config = [NSURLSessionConfigurationdefaultSessionConfiguration];
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) {
   // handle response...
}];
[sessionDataTask resume];
[session finishTasksAndInvalidate];
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!