0

Я новичок в NSOperationQueue, но в текущем проекте мне нужен способ отменить асинхронную операцию, когда он истекает. В моем исходном коде используется GCD и работает. Вот оно:NSOperationQueue, кажется, замедляет производительность

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{ 
    rg = [[RiverGauge alloc] initWithStationInfo:[station id] forHistoryInHours:48 inThisFormat:nil]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [hud removeFromSuperview]; 
     UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 
     GaugeViewController *vc = [sb instantiateViewControllerWithIdentifier:@"GaugeDetailViewController"]; 
     [vc setRiverGauge:rg]; 
     [self.navigationController pushViewController:vc animated:YES]; 
    }); 
}); 

RiverGauge это сетевая операция, которая может занять много времени, особенно если пользователь находится в сельской местности. Большинство сообщений, которые я видел, предлагают переместить это в NSOperationQueue. Я сделал это следующим образом:

NSOperationQueue *queue = [NSOperationQueue new]; 
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(getGaugeDetail:) object:[station id]]; 
[queue addOperation:op]; 

В методе getGaugeDetail (в параметре селектора) У меня есть следующий код:

RiverGauge *rg = [[RiverGauge alloc] initWithStationInfo:gaugeIdentifier forHistoryInHours:48 inThisFormat:nil]; 
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 
GaugeViewController *vc = [sb instantiateViewControllerWithIdentifier:@"GaugeDetailViewController"]; 
[vc setRiverGauge:rg]; 
[self.navigationController pushViewController:vc animated:YES]; 

При выполнении этой операции с помощью НОД, он реагирует быстро с быстрым интернет-соединение. Однако при использовании NSOperationQueue одна и та же операция занимает несколько секунд. Однако возвращенные данные точны. Опять же, я не очень хорошо знаком с NSOperationQueue. Какие-либо предложения?

Спасибо!

ответ

3

В вашем коде при использовании NSOperationQueue, похоже, что вы делаете изменения в пользовательском интерфейсе в NSOperationQueue, что, предположительно, будет выполнять их в потоке, отличном от основного потока. Иногда, когда вы взаимодействуете с элементами UIKit с основного потока, они либо работают неправильно, либо появляются заметные задержки до появления результатов. Поэтому я предполагаю, что вы видите эффекты внесения изменений пользовательского интерфейса в неправильную цепочку.

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

+0

Я бы добавил, что вы должны убедиться, что операция выполняется на фоновом потоке. –

+0

Итак, моя дилемма продолжается. Мне нужно только отменить операцию, если она истечет, и именно поэтому я пытаюсь переключиться на NSOperation, так как она, видимо, может быть отменена. Кажется, я здесь в Catch 22. – Pheepster

+0

Обратите внимание, что поддержка отмены NSOperation фактически не отменяет выполняемые операции. Он просто устанавливает «isCancelled» на «да», что ваша операция может проверить, и предотвращает запуск операции, если она еще не была выполнена. Нет ничего особенного в том, что он делает для вас. –

0

Вместо

[self.navigationController pushViewController:vc animated:YES]; 

вы можете попробовать это:

[self.navigationController performSelectorOnMainThread:@selector(someMethod) withObject:<#(id)#> waitUntilDone:NO]; 

-(void)someMethod 
{ 
//pushViewController:animated: //and other stuffs 
} 

Кроме того, если вы хотите, чтобы отменить операцию, было бы лучше, если вы используете NSOperation вместо NSOperation вместо NSInvocationOperation. Вы можете создать подкласс NSOperation, и вы можете выполнять все остальные вещи, кроме самого обновления пользовательского интерфейса. Он имеет свойство «отменить». Вы можете использовать его вместе с NSMutableURLRequest, если хотите, чтобы он был отменен для тайм-аута.

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.downloadURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0]; 

Я не знаю точно, что вы пытаетесь сделать во втором потоке отдельно от основного потока, но единственное, что вы должны иметь в виду, что вы должны отделить основной поток от других потоков , Всякий раз, когда вы имеете дело с пользовательским интерфейсом или обновляете пользовательский интерфейс, вы должны делать все это в основном потоке. Остальные манипуляции и фоновый материал могут обрабатываться отдельным потоком с помощью NSOperationQueue. В противном случае ваш пользовательский интерфейс может зависнуть. Пожалуйста, дайте мне знать, если это сработает. Спасибо :) :)

0

Прежде всего, вам не нужно перемещать это в NSOperationQueue из GCD. Это не имеет смысла. Проблема в том, что RiverGauge -initWithStationInfo: forHistoryInHours: inThisFormat: сам метод.

RiverGauge - это сетевая операция, которая может занять много времени, особенно если пользователь находится в сельской местности.

Если вы хотите отменить этот метод, вы должны реализовать что-то для отмены в методе или полностью переписать. Отделив сетевую операцию от метода и используя асинхронный API, такой как NSURLConnection + sendAsynchronousRequest: queue: completHandler: это хорошая идея. Вы можете отменить NSURLConnection асинхронно в любое время.

Смежные вопросы