NSInvocationOperation обратный вызов слишком рано

Я знаю, что подобные вопросы задавали несколько раз, но я изо всех сил пытаюсь понять, как эта конкретная проблема может быть решена. До сих пор все, что я делал, было выполнено на основном протекторе. Теперь я обнаружил, что мне нужно выполнить операцию, которая займет некоторое время, и я хочу добавить HUD к моему дисплею в течение всего времени операции и выцветать, когда операция будет завершена.

После прочтения многого о GCD (и с некоторым недоразумением), я решил, что самым простым способом было бы назвать мой многопользовательский метод с NSInvocationOperation и добавить его к недавно созданному NSOperationQueue. Это то, что у меня есть:

[self showLoadingConfirmation]; // puts HUD on screen // this bit takes a while to draw a large number of dots on a MKMapView NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(timeConsumingOperation:) object:[self lotsOfDataFromManagedObject]]; // this fades the HUD away and removes it from the superview [operation setCompletionBlock:^{ [self performSelectorOnMainThread:@selector(fadeConfirmation:) withObject:loadingView waitUntilDone:YES]; }]; NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; [operationQueue addOperation:operation]; 

Я ожидаю, что это покажет HUD, начните рисовать точки на карте, а затем, как только эта операция будет завершена , исчезнет HUD.

Вместо этого он показывает HUD, начинает рисовать точки на карте и исчезает, как HUD, все еще рисуя точки. Согласно моим NSLogs, есть около четверти второй задержки, прежде чем вызывать метод для выцветания HUD. Тем временем рисование точек продолжается еще несколько секунд.

Что я могу сделать, чтобы дождаться завершения рисования на карте до того, как исчезнет HUD?

благодаря

ДОПОЛНИТЕЛЬНО ДОБАВИТЬ:

Я почти добился успеха после внесения следующих изменений:

  NSInvocationOperation *showHud = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showLoadingConfirmation) object:nil]; NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(timeConsumingOperation:) object:[self lotsOfDataFromManagedObject]]; NSInvocationOperation *hideHud = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(fadeConfirmation:) object:loadingView]; NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; NSArray *operations = [NSArray arrayWithObjects:showHud, operation, hideHud, nil]; [operationQueue addOperations:operations waitUntilFinished:YES]; 

Как ни странно, кажется, что он сначала вызывает время для запуска, затем showLoadingConfirmation, затем fadeConfirmation. Это соответствует моим NSLogs, которые запускаются в рамках этих методов.

Поведение, которое я вижу на экране, таково: точки рисованы, и карта корректирует его масштабирование соответственно (часть timeConsumingOperation), затем отображается HUD на экране, а затем ничего. Все три NSLogs появляются мгновенно, хотя showLoadingConfirmation не происходит до тех пор, пока timeConsumingOperation не будет завершен, и fadeConfirmation, похоже, вообще не происходит.

Это кажется очень странным, но также, по-видимому, предполагает, что есть способ сделать что-то случившееся по завершении timeConsumingOperation.

Я попытался добавить это:

 [operationQueue setMaxConcurrentOperationCount:1]; 

а также это:

 [showHud setQueuePriority:NSOperationQueuePriorityVeryHigh]; [operation setQueuePriority:NSOperationQueuePriorityNormal]; [hideHud setQueuePriority:NSOperationQueuePriorityVeryLow]; 

но они, похоже, не имеют никакого значения.

Помните, что вы на самом деле делаете при установке обработчика завершения для NSInvocationOperation: когда такая операция завершается, происходит следующее (из справочника классов NSOperation):

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

Итак, во-первых, блок выполняется на вторичном потоке (поэтому он войдет в очередь этого потока, и когда его очередь наконец вернется, он просто отправит другое задание в очередь основного потока). Это означает, что он будет смешанным в такой очереди с другими ожидающими заданиями, такими как последние обновления для контактов, которые были отправлены в основную очередь из вашего timeConsumingOperation: selector.

И здесь возникает проблема: если вы не задаете приоритеты для разных заданий, невозможно описать порядок, в котором такие задания будут окончательно обработаны, даже если вы знаете, что они были отправлены раньше времени . Кроме того, в вашем случае тот факт, что завершена ваша NSInvocationOperation , не обязательно означает, что все ваши объекты были отображены на экране к тому времени, когда вызывается этот блок, это означает, что они были отправлены в поток обновления пользовательского интерфейса, который будет обрабатываться, когда их поворот приходит.

Имея это в виду и учитывая, что вы не хотите идти по пути GCD (что я бы рекомендовал дать еще одну попытку, поскольку я знаю, что это нелегко в начале, но когда вы это понимаете, вы понимаете, что это замечательное решение для почти всех многопоточных материалов, которые вы хотели бы сделать на iPhone), я бы создал NSOperationQueue и отправил бы все задания там (тот, который удаляет включенную HUD, но с более низким приоритетом, чем другие). Таким образом, вы убедитесь, что удаление HUD обрабатывается в главной очереди после завершения всех заданий «pin», что было вашей первой целью.

Если HUD исчезает, тогда вызывается ваш блок завершения, что означает timeConsumingOperation: вашего метода timeConsumingOperation: .

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

  • NsoperationQueue Отмена всех операций не отменяется до завершения операции
  • AFNetworking setUploadProgressBlock не ведет прямо к 100%
  • Добавление изображений в массив последовательно
  • Отключение openURL, когда приложение запускается через удаленный push через didFinishLaunchingWithOptions
  • Отменить NSOperation in for loop?
  • Вызов - (void) cancelAllOperations в NSoperationQueue не устанавливает свойство isCancelled NSOperation, которое присутствует внутри очереди
  • Отменить NSOperation из NSOperationQueue
  • NSOperation блокировки и блоки NSOperationQueue
  • Управление связкой NSOperation с зависимостями
  • dispatch_after эквивалент в NSOperationQueue
  • NSOperationQueue с таймером
  • Давайте будем гением компьютера.