Прекратите выполнение анимации в фоновом потоке и выполните цикл - PullRequest
2 голосов
/ 19 декабря 2011

Я запускаю свои анимации в UITAbleViewCell. Каждая ячейка имеет свою собственную анимацию, и ячейки можно использовать повторно. Я использую [mView performSelectorInBackground:@selector(layoutSubview) withObject:nil];

Там в фоновом потоке я запускаю runLoop для выполнения таких задач:

- (void)startAnimation 
{
    NSRunLoop *mLoop = [NSRunLoop currentRunLoop];
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];

    mRunLoop = YES;
    while (mRunLoop == YES && [mLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]);
}

и остановите это:

- (void)stopAnimation 
{
    if (![NSThread isMainThread]) {
        [[NSThread currentThread] cancel];
    }

    mRunLoop = NO;
    self.animationTimer = nil;
    CFRunLoopStop(CFRunLoopGetCurrent());   
}

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

сообщение отправлено на освобожденный экземпляр

Так, не могли бы вы дать мне несколько советов, как мне правильно прекратить выполнение операций в этом потоке? Я имею в виду, если я хочу повторно продать, например, объект, который выполняет некоторые действия, как немедленно остановить их?

Надеюсь, я дал достаточно информации. Спасибо

ОБНОВЛЕНИЕ: Нет идей вообще?

Ответы [ 4 ]

2 голосов
/ 28 декабря 2011

NSTimer имеет метод -cancel, который останавливает срабатывание таймера.Вызов его в -prepareForReuse (и, в этом отношении, в -stopAnimation) может помочь.

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

(Если вы отбросите материал runloop в -startAnimation, не останавливайте runloop в -stopAnimation.)

Что-то вроде того, что рекомендует danyowdee, будет еще лучше, но, по крайней мере, избавьтесь от этой вещи runloop.Это просто напрашивается на неприятности.

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

Я возьму совершенно другой удар:

Избавьтесь от таймеров ячейки и фоновых потоков в целом!

Анимация - это не то, где NSTimer хорошо подходит в первую очередь, и наличие нескольких таймеров также не поможет.

UITableView имеет метод visibleCells и метод indexPathsForVisibleRows. Я бы предложил использовать один CADisplayLink - который подходит для анимации, так как он вызывает вас обратно с фактической частотой обновления дисплея или ее доли - в вашем tableview-controller и в обратном вызове этого display-link перебирают видимые ячейки.

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

Код:

@interface AnimatedTableViewController ()

@property (strong, nonatomic) CADisplayLink *cellAnimator;
- (void)__cellAnimatorFired:(CADisplayLink *)animator;

@end

@implementation AnimatedTableViewController

@synthesize cellAnimator = cellAnimator_;

- (void)setCellAnimator:(CADisplayLink *)animator
{
    if (animator == cellAnimator_)
        return;

    [cellAnimator_ invalidate];
    cellAnimator_ = animator;

    [cellAnimator_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSCommonRunLoopModes];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    self.cellAnimator = [CADisplayLink displayLinkWithTarget:self selector:@selector(__cellAnimatorFired:)];
    ...
}

- (void)viewWillDisappear:(BOOL)animated
{
    self.cellAnimator = nil;
    ...

    [super viewWillDisappear:animated];
}

- (void)__cellAnimatorFired:(CADisplayLink *)animator
{
     NSArray *visibleCells = [self.tableView visibleCells];
     [visibleCells enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger unused, BOOL *stop){
         [cell setNeedsDisplay];
     }];
}

...

@end
0 голосов
/ 28 декабря 2011

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

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

Только еще одно соображение, я не рекомендую связываться с несколькими cfrunloopstop, протестируйте это сильно!

0 голосов
/ 20 декабря 2011

Я думаю, вы можете использовать этот метод для вашей проблемы

[NSObject cancelPreviousPerformRequestsWithTarget:yourTarget selector:aSelector object: anArgument];

...