3

When using NSURLConnection, you had the option to schedule the connection using NSRunLoop:

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode

Passing NSDefaultRunLoopMode would effectively cause the connection to pause if the user scrolled, which was great for performance as the user experience was never impacted by the download.

Is there a way to get similar behaviour for NSURLSession? I have read through the docs and tried various ways of configuring the session with no success.

Gadd
  • 61
  • 1
  • 6

2 Answers2

3

NSURLSession works at an "upper" level, and is made to be more simple for the developer than using NSURLConnection.
I made some tests and I think that there is no possibility to control the runloop and the mode of NSURLSession, because they seems to be managed by an external daemon and not by your App (I tested only with NSURLSessionDownloadTask).
Do this simple test:

  1. download and execute this Github project
  2. start a download
  3. open the "downloads" controller to see the state of your download
  4. pause the app
  5. wait a moment
  6. unpause the app

you will see that the download has been continued while your app was paused, so when you start a NSURLSession the control is passed to the system, outside your app: it means that the main part of the work doesn't take place in an internal runLoop.

The only thing you have control on, is the serial queue on which the delegate calls are dispatched (passed back to your application). The delegate calls are queued for execution (on the main or on a background thread, you can choose it), since NSOperationQueue use Grand Central Dispatch to queue the calls, I'm not sure about the runloop mode used for this, but I think this is a good starting point to continue your researches.

EDIT: If I remember correctly, dispatch calls made on background threads are made on threads where a runloop is not running. In fact, if you add this line

NSLog(@"%@", [[NSRunLoop currentRunLoop] currentMode]);

on one of the delegate methods of the FLDownloader class in the previous project, you will see that there is no run mode (nil), this happens when the runloop is not running.

LombaX
  • 17,265
  • 5
  • 52
  • 77
  • You are using a background session configuration, which is causing your tasks to be managed off-process. Non-background sessions operate on threads managed by the app. I agree that it doesn't seem possible to do what I want. – Gadd Dec 01 '13 at 17:12
  • I'm not sure about this, but I was curious about the argument and made some researches. If you analyze the entire stack trace of a newly created NSURLSession (non-background), there are some method calls that belongs to runloops. For example `[NSURLConnection, _resourceLoadLoop:]` and `[NSURLConnection, resourceLoaderRunLoop]`. I tried to swizzle the second, and it gave me access to a CFRunLoop object (as the return value). Maybe you can try to cheat with it and change the mode of the NSURLSession (if I understand correctly, it seems that NSURLSession uses kCFRunLoopDefaultMode) – LombaX Dec 02 '13 at 17:50
  • Interesting, I saw the resourceLoadLoop in traces as well but didn't look into it more. I don't want to make a hack for this though, if it's not supported I'll just stick to the standard behavior of running in default mode. – Gadd Dec 03 '13 at 13:01
0

If anyone ever runs into this problem again. You can't schedule the download task into a different run loop but you can handle the response in a different run loop mode which still greatly improves the performance while scrolling.

[self performSelectorOnMainThread:@selector(requestDidFinishLoadingWithData:)
                       withObject:data
                    waitUntilDone:YES
                            modes:@[NSDefaultRunLoopMode]];
Mikrohard
  • 211
  • 2
  • 3