UIView анимация заблокирована занятым основным потоком - PullRequest
1 голос
/ 10 марта 2011

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

[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];

for( int i = 0; i < 1000; i++ ) {
   NSLog(@"Performing heavy operation...");
}

[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];

Что происходит в первой строке, так это то, что моему представлению загрузчика присвоено значение, добавлено подчиненное отображение и сказано, что оно затухает с помощью стандартной анимации UIView. Однако анимация не запускается (как показано setAnimationWillStartSelector:) до тех пор, пока после тяжелая операция не будет завершена.

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

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

Мой вопрос: Можно ли делать то, что я хочу, и / или мне вообще это надо?

Ответы [ 2 ]

2 голосов
/ 10 марта 2011

В качестве альтернативы предложению Джошуа Смита, в случае, если нахождение в другом потоке мешает вашей работе, просто убедитесь, что вы выпадаете в runloop между запуском анимации UIView и запуском тяжелого кода. Э.Г.

    ...
    [[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
    [self performSelector:@selector(performHeavyOperation) withObject:nil afterDelay:0];
}

- (void)performHeavyOperation
{
   for( int i = 0; i < 1000; i++ ) {
      NSLog(@"Performing heavy operation...");
   }

   [[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
}

executeSelector: withObject: afterDelay: заставляет назначенный по номеру селектор планироваться в цикле выполнения в будущем. Установка задержки в 0 означает, что она добавляется в цикл запуска, чтобы произойти как можно скорее.

По разным причинам довольно много UIView вступает в силу только в том случае, если вы позволите стеку вызовов полностью развернуться до стека вызовов. Это так, например, если вы сделали:

view.frame = aNewFrame;
view.someOtherPropertyThatWouldCauseAVisibleChange = anotherValue;

Тогда UIView перерисовывает себя только один раз, а не дважды.

1 голос
/ 10 марта 2011

Поместите вашу тяжелую операцию в NSOperationQueue, тогда она не заблокирует основной поток.

@interface MyClass : NSOperation {

}

@end


@implementation MyClass

-(void) main {

    for( int i = 0; i < 1000; i++ ) {
        NSLog(@"Performing heavy operation...");
    }    

}

@end

Тогда в приведенном выше коде:

[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
NSOperationQueue *q = [[NSOperationQueue alloc] init];
MyClass *c = [[[MyClass alloc] init] autorelease];
[q addOperation:c];
[q waitUntilAllOperationsAreFinished];

[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];

Читайте документы тоже, они вам понадобятся: http://developer.apple.com/library/mac/#documentation/cocoa/reference/NSOperationQueue_class/Reference/Reference.html

NSOperationQueue удивительные, но не совсем интуитивно понятные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...