I'm trying to find a way to prevent NSURLSession from caching responses (in Simulator) by using approach from these questions:
Prevent NSURLSession from caching responses
NSURLCache does not clear stored responses in iOS8
I don't want to use approach from this question:
Clear NSURLSession Cache
which is using ephemeralSessionConfiguration call to NSURLSessionConfiguration when setting up a new NSURLSession instance.
I just want to not cache requests, in worst case clear the cache when application resigns active.
With call [NSURLCache sharedURLCache] removeAllCachedResponses] in applicationWillResignActive execution in app delegate i still get Cache.db present and full of cached responses/requests in respective iOS Simulator cache on disk.
Before that, I changed all my NSURLSessionConfiguration cache policy to NSURLRequestReloadIgnoringCacheData NSURLRequestReloadIgnoringLocalCacheData
NSURLRequestReloadIgnoringLocalAndRemoteCacheData
(tested all three cases, the last one is probably not implemented) instances (and effectively ALL cache policies). Isn't changing cache policy supposed to result in not caching server responses and requests on simulator? Does it depends on storage policy in response headers?
I'm using iOS 9.3 and iOS 10.1 Simulators for iPhone and v. 10 of iOS SDK. Project is in Objective C, maybe full Swift project would behave differently?
Is this behavior different for simulators and with devices? Why is this happening, is there a solution different from using dependency (can't fall onto one right now for some reason).
I had a similar issue and I was able to fix this by setting the properties- URLCache and requestCachePolicy on NSURLSessionConfiguration to nil and NSURLRequestReloadIgnoringCacheData respectively.
Also, you can try setting the cachePolicy property on NSMutableURLRequest to NSURLRequestReloadIgnoringCacheData.
So the rude solution is ... drop the Cache.db file on app resigning. I assume on the simulator you should be able to?!
But, did you try the ephemeralSessionConfiguration? Because Apples header doc says:
* An ephemeral session has no persistent disk storage for cookies,
* cache or credentials.
Related
This My Test cases, point out that when using NSURLSession with a HTTP/2 connection there is memory problem.
test1: iOS 9. HTTP/2 server
I use NSURLSession to upload 10M file to a HTTP/2 server, if the file uploaded completed everything is ok, But if I cancel the upload task before it's completed, the 10M will never release.
test2: iOS 9. HTTPs1.1 server
I test the same code with a https1.1 file server, I cancel the upload task or not, everything is ok, the memory back to normal.(10M data is released)
test3 iOS 8. HTTP/2 server
This case everything is ok.(NSURLSession did not protocol negotiation to HTTP/2)
So, Even there is some thing not appropriate with my using NSURLSession, NSURLSession performance is not normal with HTTP/2.
Besides memory problem, when using NSURLSession with HTTP/2 to uploading file the progress segment size is huge(May 2M 'didSendBodyData' at one call back)
I also had read this page. SSL may cache some thing, but should not cache the whole file.(When I cancel the task or request timed out, 10M file size memory leaks)
Anyone Knows what cause the problem, could give me some help.
Thanks.
Question update 0912: add a test project link
Test project :https://github.com/upyun/swift-sdk/tree/testleak
file:UPUtils.swift
//Change the url to make comparison test.
//let DEFAULT_UPYUN_FORM_API_DOMAIN = "http://v0.api.upyun.com"//http1.1
//let DEFAULT_UPYUN_FORM_API_DOMAIN = "https://httpbin.org/post" //https1.1
let DEFAULT_UPYUN_FORM_API_DOMAIN = "https://v0.api.upyun.com"//http2
From apple doc:
The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you don’t invalidate the session, your app leaks memory until it exits.
Also looking at your project https://github.com/upyun/swift-sdk/tree/testleak you need to call finishTasksAndInvalidate() after sessionTask.resume() since you are creating session per request
I have an app working just fine on iOS 9 with a Custom NSURLProtocol implemented with NSURLSession. All the requests executed by the client are done with NSURLSession as well and each sessionConfiguration is registering to the protocol before executing the request.
I have an issue with iOS 8 that I don't have with iOS 9. With iOS8 the Custom NSURLProtocol is performing that request non stop, basically an infinite loop of the same request. canInitWithRequest: in the custom protocol gets called way more on iOS 8 than on iOS 9 which basically drives the method startLoading to get called and fire the request after some header modifications that my protocol is supposed to perform.
Is there a known issue with iOS8 where NSURLProtocols and NSURLSession don't behave as expected?
I'm not aware of any problems like that, no.
This sounds like your protocol is either:
Failing to detect its own header modifications for some reason
Failing to make the modifications for some reason
Failing to return NO when your protocol is asked if it should handle a request that already contains the modified headers
Be sure that you are using a header field for the purpose of detecting whether to handle the request. There's no guarantee that the specific NSURLRequest object that you pass down into the machinery will come back to you. I'm not even sure I would trust propertyForKey:inRequest:, but that might make me too paranoid.
Critically, don't ever subclass NSURLRequest when working with NSURLSession. In iOS 9, it almost works. The farther back you get, the more broken the behavior, until by iOS 7 you're pretty much relegated to using NSURLConnection. :-)
My code is the following:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *theURLString = #"http://website.com/musicFile";
NSData *theData = [NSData dataWithContentsOfURL:[NSURL URLWithString:theURLString]];
}
There is nothing special at all. I am not even using the background thread.
Here is the behavior I get on iOS 8.x (and the behavior that I expect to get):
So, NSData is fully released and all of the occupied memory is back.
However, iOS 9.x surprised me a lot:
My questions are:
Approximately 100 MB are gone for nothing in iOS 9.x. How can I get them back? Are there any workarounds?
iOS 8.x has occupied 136.2 MB at max, while iOS 9.x used 225.9 MB at max. Why is this happening?
What is going on in iOS 9.x?
UPDATE #1:
I have also tried using NSURLSession 'dataTaskWithURL:completionHandler:' (thanks to #CouchDeveloper). This reduces the leak, but doesn't fully solve the problem (this time both iOS 8.x and iOS 9.x).
I used the code below:
NSURLSession *theURLSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionDataTask *theURLSessionDataTask = [theURLSession dataTaskWithURL:[NSURL URLWithString:theURLString]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
{
NSLog(#"done");
}];
[theURLSessionDataTask resume];
As you can see, 30 MB are still lost.
UPDATE #2:
The above tests where done using Xcode simulator.
However, I have also decided to test on actual iOS 9.2 iPhone 4S (as recommended by #Sohil R. Memon).
The results of 'NSData dataWithContentsOfURL:' are below:
The results of using 'NSURLSession dataTaskWithURL:completionHandler:' are below:
It looks like 'NSData dataWithContentsOfURL:' works perfectly on actual device, while 'NSURLSession dataTaskWithURL:completionHandler:' -- doesn't.
However, does anyone know solutions which show identical information on BOTH actual device AND Xcode simulator?
Approximately 100 MB are gone for nothing in iOS 9.x. How can I get them back? Are there any workarounds?
For a couple of reasons, we should use NSURLSession to download data from a web service. So, this is not a workaround, but the correct approach.
What is going on in iOS 9.x?
I have no idea - possibly cached data, network buffers, or some other issues. But this is irrelevant - you should try the correct approach with NSURLSession.
From the docs:
IMPORTANT
Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the dataTaskWithURL:completionHandler: method of the NSURLSession class. See URL Session Programming Guide for details.
Edit:
Those "reasons" are:
NSURLSession is specifically designed to load remote resources.
NSURLSession methods are asynchronous which is crucial for methods which complete only after a perceivable duration (it will not block the calling thread).
A session can handle authentication by means of a default method or with a custom delegate.
Session tasks can be cancelled.
Here is also an answer which helped me. The answer states to use [NSData dataWithContentsOfURL:url options:0 error:&error]; instead.
Hope this helps
Encountered a very weird problem, using simple AFNetworking downloading operation, even tried with simple NSURLConnection operation, connection fails if you keep your app running, and lock screen and then unlock. Works absolutely fine in background though.
Any one encountered similar problem with NSURLConnection want to share some solution?
Thanks.
It looks like an iOS bug. Weird, but lock screen action affects NSURLSession somehow, so that it stops working and returns NSURLErrorNetworkConnectionLost. So in my app I gave up using shared session. I either use a new session object for every request or (if I need to maintain one session constantly) recreate it every time the screen gets unlocked. And it works. For users of AFNetworking or any other third party library working on top of NSURLSession the situation is harder, of course. You'll need to correct the code of the library, which is definitely not a good thing, but I think there's no other choice
Very helpful Andrey Chernukha,
In my case, figured out that you don't necessary need to recreate new session every time.
I ended up using array to save running NSURLSessionDataTasks and after phone is unlocked resume them.
Steps:
I created array NSMutableArray *dataTasksToResume
In - (void)applicationWillResignActive:(UIApplication *)application I saved all tasks to dataTasksToResume array
Cancel all running NSURLSessionDataTasks
In - (void)applicationDidBecomeActive:(UIApplication *)application get all tasks from array and resuming them (re-creating them)
Enjoy!
Hope it helps.
I want to use NSOperationQueue in my app and start downloading the images from server.
If my app goes to background or be terminated, will NSOperationQueue still continues downloading them?
No, the NSOperationQueue will not continue working when the app is moved to the background. You would need to explicitly action this by using the method beginBackgroundTaskWithExpirationHandler.
This is covered in Technical Note TN2277 - Networking and Multitasking
See also iOS App Programming Guide, specifically the section on App States and Multitasking.
As an aside, could I recommend that you instead use the AFNetworking library. It handles a lot of this functionality for you. Specifically, each class is a subclass of NSOperation.
Moreover, it already has an image downloader class in AFImageRequestOperation. So that should be very useful for you. AFImageRequestOperation is a subclass of AFURLConnectionOperation so you have access to the method setShouldExecuteAsBackgroundTaskWithExpirationHandler.
Of course, all this relates to multitasking so it is only available in iOS 4.0 and later.