2011-12-19 36 views
2

我在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循环之前,单元格从视图中消失,并且已可供重用。于是,我开始清理它,而周期仍在进行操作,在这里我遇到像

消息的情况下发送到释放实例

所以,请你给我,我应该如何正确地停止一些提示在该线程中执行操作?我的意思是,如果我想举个例子来说明一个对象,这个对象正在执行一些如何立即停止他们的动作?

希望我给了足够的信息。 谢谢

更新:没有想法吗?

+0

呃,除少数情况外,你不应该从任何地方向主线程发送消息给UIKit中的任何东西。 UITableViewCell不是线程安全的。 – 2011-12-19 22:11:55

+0

Yeap我知道,我在主线程中执行的所有UIKit操作,但动画,石英动画(图表绘制)不在后台线程中。 – 2011-12-20 07:25:15

+0

哦对。请原谅我无法阅读。 – 2011-12-20 15:06:12

回答

2

我会采取完全不同的刺就可以了:

获得完全摆脱电池的定时器和后台线程的!

动画不是NSTimer很适合放在首位,并且有多个定时器也无济于事。

UITableView具有方法visibleCells和方法indexPathsForVisibleRows。我建议使用单一的CADisplayLink - 其中适合动画,因为它会以显示器的实际刷新率或其一部分回显您的表格视图控制器和该显示器链接的回调迭代可见单元格。

如果您想要在辅助线程的运行循环上安排显示链接,请随时这样做,但我会检查是否可以在没有额外线程的情况下先离开。

一些代码:

@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

我想你可以用这个方法您的问题

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

+0

文档说:“取消执行先前使用performSelector:withObject:afterDelay:注册的请求。”我使用performSelectorInBackground执行选择器 – 2011-12-20 07:04:29

+0

现在工作吗? – Emon 2011-12-20 08:10:47

+0

不,如我所说,它不适合我的情况。 – 2011-12-20 08:24:24

0

我认为要避免这种行为,最好的办法是分配接收在其他类中的方法取消将不被重用的委托。例如,您可以拥有处理所有取消方法的私有数组实例,每行都映射到一个数组元素。

我向你推荐Apple在Xcode文档中提供的懒表示例。这是一个很好的例子,介绍如何在后台使用表格异步加载图像。我认为这对滚动主题(减速和分页)也是有用的。

只有一个考虑,我不建议搞乱几个cfrunloopstop,测试它很难!

2

NSTimer有一个-cancel方法来停止定时器发射。在-prepareForReuse(对于这个问题,在-stopAnimation中)调用它可能会有所帮助。

但是,这段代码看起来相当危险。像这样嵌套运行循环几乎不是一个好主意 - 而且,据我所知,这是完全没有必要的。如果您让-startAnimation返回,您的动画计时器仍将在主运行循环中运行。如果你这样做,因为你想延迟-startAnimation之后的代码,你应该重构你的代码,这样就不需要了。

(如果你在-startAnimation降runloop东西,不-stopAnimation停止runloop无论是。)

喜欢的东西的方法danyowdee建议甚至会更好,但至少摆脱这种runloop东西。这只是要求麻烦。