Использование Runloop внутри nsoperation для асинхронных задач

У меня есть вариант использования, когда мне нужно загрузить много файлов с помощью NSURLSession.

Чтобы предотвратить истечение времени ожидания сеансовых задач, мне нужно поместить их в очередь операций и ограничить количество одновременных загрузок, чтобы они не голодали.

Моя идея состоит в том, что я буду загружать резюме задачи в nsoperation и загружать их в nsoperationqueue, что ограничит количество одновременных действий.

Проблема в том, что когда я вызываю [задачу возобновления], код завершится, и nsoperation будет считать себя завершенной, даже если я жду завершения загрузки файла.

Вот некоторый код.

NSURLSessionDownloadTask *task = [session downloadTaskWithRequest: request 
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

     //move the file to a permanent location

     CFRunLoopStop(CFRunLoopGetCurrent());
}
//I have instantiated a class global NSOperation queue.
[imageDownloadQueue addOperationWithBlock:^void() {
     [task resume];
     CFRunLoopRun();
}];

Как я могу сохранить поток nsoperation живым, пока я жду обратного вызова? Я также пробовал использовать nsrunloop и добавлять порт в NSMachPort. Это не помогло.

Кроме того, установка HttpMaximumConnectionsPerHost в NSURLSession не помогает, поскольку таймер тайм-аута запускается при возобновлении задачи. А это значит, что я получу больше тайм-аутов, чем раньше.


person mac10688    schedule 22.09.2016    source источник
comment
См. developer.apple.com/videos/play/wwdc2015/226 ( WWDC 2015 Advanced NSOperations), в котором подробно обсуждаются подобные вопросы. Год спустя я спросил Дэйва Делонга об этих шаблонах, чтобы узнать, считает ли он, что они все еще работают. Он говорит, что это продолжает быть очень успешным.   -  person Rob Napier    schedule 23.09.2016
comment
Это было хорошее видео, но у меня нет времени переписывать выкройку. Это намного больше кода, чем это. Я просто хочу, чтобы поток оставался в живых, а обратный вызов удалялся.   -  person mac10688    schedule 23.09.2016
comment
Для этого и нужны асинхронные NSOperations. Вы создаете собственный NSOperation, возвращаете true вместо isAsynchronous и переопределяете start() вместо main(). Вам нужно будет установить isExecuting и isFinished вручную в соответствующее время (например, когда ваша операция завершится). Вы не хотите, чтобы нить оставалась в живых. Вы хотите, чтобы операция отметила себя завершенной в нужное время. Вот как работают асинхронные операции. Это просто означает, что вы делаете это вручную, а не когда main() возвращается.   -  person Rob Napier    schedule 23.09.2016


Ответы (1)


К сожалению, у меня не было времени на правильное исправление, и мне нужно было исправить проблемы с тайм-аутом как можно скорее. Я нашел кое-что, что действительно работало.

    //I have instantiated a class global NSOperation queue.
[imageDownloadQueue addOperationWithBlock:^void() {
     [task resume];
     while(task.state == NSURLSessionTaskStateRunning)
    {
         [NSThread sleepForTimeInterval:.1];
    }
    }];

Поскольку NSOperation работает в фоновом потоке, я смог приостановить этот поток во время выполнения задачи. Это предохраняет поток от смерти, но задача будет выполняться в своем собственном потоке и не блокироваться.

person mac10688    schedule 24.09.2016