iOS, NSURLConnection: делегировать обратные вызовы в другом потоке? - PullRequest
8 голосов
/ 22 ноября 2011

Как я могу получить NSURLConnection для вызова его методов делегата из другого потока, а не из основного потока.Я пытаюсь возиться с scheduleInRunLoop: forMode: но, кажется, не делает то, что я хочу.

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

NSURLRequest * request = [NSURLRequest requestWithURL:url];
NSURLConnection * connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSRunLoop * loop = [NSRunLoop currentRunLoop];
NSLog(@"loop mode: %@",[loop currentMode]);
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];

Другая вещь, которую я не вижу, большая часть - это «Режимы». Есть только два задокументированных режима, так что не так уж и много для тестирования.

Любые идеи?

Спасибо

Ответы [ 4 ]

6 голосов
/ 26 декабря 2011

Существует несколько вариантов:

  1. В вашей реализации методов делегата используйте dispatch_async.
  2. Запустите расписание соединения в фоновом потоке.

Вы можете сделать последнее следующим образом:

// all the setup comes here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSRunLoop *loop = [NSRunLoop currentRunLoop];
    [connection scheduleInRunLoop:loop forMode:NSRunLoopCommonModes];
    [loop run]; // make sure that you have a running run-loop.
});

Если вы хотите гарантировать, в каком потоке вы работаете, замените вызов на dispatch_get_global_queue() соответствующим образом.

2 голосов
/ 25 июня 2012

Если вы хотите загружать файлы в отдельном потоке, я уверен, что вы ищете именно этих дроидов ...

- (void) dispatchRequest{
    self->finished = NO;
    NSMutableURLRequest* request = //Formulate your request
    NSThread* download_thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadThreadLoop:) object:request];
    [download_thread start];
}
- (void) downloadThreadLoop:(NSMutableURLRequest*) request{

    NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    while(!self->finished]){
        //This line below is the magic!
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    //...
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    //...
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    //...   
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    //...   
    self->finished = YES;
}
0 голосов
/ 22 ноября 2011

NSURLConnection уже выполняет асинхронную загрузку основного потока. Если я понимаю ваш вопрос, вы хотели бы, чтобы сообщения делегатов отправлялись в потоке, отличном от основного потока? Вы не можете сделать это, так как вы не можете изменить внутреннюю реализацию NSURLConnection. Я могу придумать два способа симулировать это.

  1. Создайте подкласс NSURLConnection (например, MyURLConnection), который назначает себя в качестве собственного делегата. Обратите внимание, что это создает намеренный цикл сохранения, поэтому будьте осторожны. MyURLConnection должен определить новый delegate, который поддерживает NSURLConnectionDelegate. Давайте назовем это finalDelegate. Когда MyURLConnection обрабатывает свои собственные сообщения делегатов, переадресуйте или отправьте их на finalDelegate в любом потоке, который вам нравится.

  2. Аналогично варианту № 1, но без подкласса. Обработайте методы делегата NSURLConnection в главном потоке и отправьте / отправьте их в любой поток, какой захотите.

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

EDIT: добавлено предложение по запуску кода в фоновом режиме

Если вы собираетесь обрабатывать ответ в фоновом режиме, я бы использовал операции или грандиозную центральную диспетчеризацию. Не нужно возиться с циклами выполнения и созданием потоков. Ознакомьтесь с руководством по программированию параллелизма Apple .

0 голосов
/ 22 ноября 2011

Если вам действительно необходимо выполнить загрузку в новом потоке, может быть проще detachNewThreadSelector:toTarget:withObject:, настроить (и уничтожить) NSAutoreleasePool, а затем использовать один из синхронных селекторов, таких как NSData 's dataWithContentsOfURL:. Это не будет использовать асинхронный NSURLConnectionDelegate.

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

...