Подтвердить что ты не робот

Предупреждение о отключении CoreAnimation с незафиксированным CATransaction

У меня возникают проблемы со следующим предупреждением:

CoreAnimation: предупреждение, удаленная нить с незафиксированным CATransaction; установите CA_DEBUG_TRANSACTIONS = 1 в среде для регистрации обратных трасс.

Я использую объект NSOperation для выполнения некоторых вычислений, после его завершения он отправляет сообщение обратно в AppDelegate, который затем скрывает индикатор выполнения и отображает некоторые кнопки. Если я прокомментирую сообщение обратно в AppDelegate, предупреждение исчезнет, ​​но индикатор выполнения останется видимым и анимированным.

Я использую xCode 4.4.1 и OSX 10.8.1, однако, когда я компилирую и запускаю код с использованием той же версии xCode в OSX 10.7.4, я не получаю предупреждение, и код работает как ожидалось.

Настройка переменной среды CA_DEBUG_TRANSACTIONS = 1 показывает обратную трассировку, поступающую из сообщения NSControl setEnabled в AppDelegate.

Ответ, вероятно, смотрит мне в лицо, но, возможно, у меня было слишком много кофе!

4b9b3361

Ответ 1

Ваши подозрения верны. Если NSOperation завершится до завершения CoreAnimation, вы получите хорошее предупреждение:

* CoreAnimation: предупреждение, удаленная нить с незафиксированным CATransaction; установите CA_DEBUG_TRANSACTIONS = 1 в среде для регистрации обратных трасс. *

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

Решение, которое я использую, прост: в блоке или NSOperation, который запрашивает работу с CoreAnimation, я проверяю, что работа действительно была завершена до выхода.

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

^{

   // 1. Creating a completion indicator

   BOOL __block animationHasCompleted = NO;

   // 2. Requesting core animation do do some work. Using animator for instance.

   [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
      [[object animator] perform-a-nice-animation];
   } completionHandler:^{
      animationHasCompleted = YES;
   }];

   // 3. Doing other stuff…

   …

   // 4. Waiting for core animation to complete before exiting

   while (animationHasCompleted == NO)
   {
       usleep(10000);
   }

}

Ответ 2

В соответствии со стандартными парадигмами Cocoa, рекомендуемое решение здесь - выполнить работу с Core Animation в основном потоке, легко сделанное с помощью GCD:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.delegate redrawSomething];
});

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

Некоторые фреймворки, такие как Core Location, вызывают сообщение журнала, если они вызываются из любого контекста, отличного от основного потока. Другие будут генерировать загадочные сообщения, например, ваш пример с Core Animation.

Ответ 3

Другой способ обеспечения того, чтобы любой рисунок пользовательского интерфейса выполнялся в основном потоке, как описано в Numist, использует метод performSelectorOnMainThread:withObject:waitUntilDone: или, альтернативно, performSelectorOnMainThread:withObject:waitUntilDone:modes:

- (void) someMethod
{
    [...]

    // Perform all drawing/UI updates on the main thread.
    [self performSelectorOnMainThread:@selector(myCustomDrawing:)
                           withObject:myCustomData
                        waitUntilDone:YES];

    [...]
}

- (void) myCustomDrawing:(id)myCustomData
{
    // Perform any drawing/UI updates here.
}


Для связанного сообщения о различии между dispatch_async() и performSelectorOnMainThread:withObjects:waitUntilDone: см. В чем разница между performSelectorOnMainThread и dispatch_async в главной очереди?