2011-12-19 35 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東西。這只是要求麻煩。