请查看下面的更新…尽管最初这似乎是一个动画问题,但事实证明这是一个通知问题。要注意:即使您已经丢弃了对象,
我第一次使用基于块的动画,并遇到一个情况,即“完成”块似乎会在“动画”块的一次触发中被调用多次。我不明白为什么会发生这种情况,但似乎在游戏运行一段时间后会经常发生。以下是相关代码...
请注意,从第二步开始的第4行之后,日志报告了第6行上第一步的“未完成”状态,然后在第7行上完成了第二步。但是,第一步已经在第3行上完成了。它怎么可能在第3行和第6行都完成了呢?在这种情况下,一个使用finished = YES完成,另一个使用finished = NO完成,但我也看到过单个步骤记录了两个或更多finished = YES的情况。
什么会导致这样的情况发生?我甚至不确定该如何查找错误,或者也许我只是不理解iOS动画的完成块的性质。
奇怪的是,这段代码对于一个“游戏”来说完全正常,但是一旦应用程序在同一次运行中启动新的“游戏”,代码就开始产生更多的“完成”命中,我每开始一个游戏,我的动画就会有更多的“完成”命中。感觉像是一些残留垃圾互相干扰,但静态分析器中看不到任何泄漏。我感到困惑。
更新和解决方案
这个问题是由于旧的废弃游戏版仍在监听移动棋子的通知而引起的。即使旧版被释放了,当游戏结束时它们没有明确地删除通知请求。由于系统不会立即丢弃已释放的对象,因此这些“幽灵”版仍在监听全局通知以移动棋子。即使它们对我们来说已经“死了”,但它们足够活跃,可以响应并争夺棋子的动画!
解决方案是在我们释放之前让每个版
NSNotification
仍将被观察到。一定要removeObserver:
以避免这种情况发生。我第一次使用基于块的动画,并遇到一个情况,即“完成”块似乎会在“动画”块的一次触发中被调用多次。我不明白为什么会发生这种情况,但似乎在游戏运行一段时间后会经常发生。以下是相关代码...
- (void)player:(Player *)player takeStep:(NSInteger)step onWalk:(NSArray *)walk {
if (step < walk.count) {
// take this step
NSLog(@"taking step %i", step);
NTTileView *tile = [walk objectAtIndex:step];
[UIView animateWithDuration:0.5 animations:^{
NSLog(@"animating step to tile %@",tile);
[player.pawn moveToTile:tile];
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"UseAudio"]) [boombox play];
} completion:^(BOOL finished){
if (finished) {
NSLog(@"step %i done, moving on",step);
[self player:player takeStep:step+1 onWalk:walk];
} else {
NSLog(@"step %i unfinished, jumping to end",step);
NTTileView *lastTile = [walk lastObject];
[player.pawn setCenter:lastTile.center];
}
}];
}
}
这是一个递归方法,最初使用步骤=1和“tiles”数组启动,玩家的棋子可以在其中进行动画。完成每个步骤后,递归调用该方法以进行下一步。当数组用尽时,任务完成。在完成的块中,我们要么进行下一步,要么(如果动画未完成)跳转到最后一个tile。以下是一次运行的日志...
1...[79719:1be03] taking step 1
2...[79719:1be03] animating step to tile <NTTileView: 0x957cd30; baseClass = UIControl; frame = (273 260; 57 54); layer = <CALayer: 0x957cc90>>; id = 31; type = TileTypeMethods; isHub = 0; isEndSpot = 0
3...[79719:1be03] step 1 done, moving on
4...[79719:1be03] taking step 2
5...[79719:1be03] animating step to tile <NTTileView: 0x957d3b0; baseClass = UIControl; frame = (268 202; 60 59); layer = <CALayer: 0x957d2e0>>; id = 30; type = TileTypeTermsAndTheory; isHub = 0; isEndSpot = 0
6...[79719:1be03] step 1 unfinished, jumping to end
7...[79719:1be03] step 2 done, moving on
8...[79719:1be03] taking step 3
9...[79719:1be03] animating step to tile <NTTileView: 0x957dbc0; baseClass = UIControl; frame = (268 139; 59 64); layer = <CALayer: 0x957db40>>; id = 29; type = TileTypePeople; isHub = 0; isEndSpot = 0
10...[79719:1be03] step 3 done, moving on
请注意,从第二步开始的第4行之后,日志报告了第6行上第一步的“未完成”状态,然后在第7行上完成了第二步。但是,第一步已经在第3行上完成了。它怎么可能在第3行和第6行都完成了呢?在这种情况下,一个使用finished = YES完成,另一个使用finished = NO完成,但我也看到过单个步骤记录了两个或更多finished = YES的情况。
什么会导致这样的情况发生?我甚至不确定该如何查找错误,或者也许我只是不理解iOS动画的完成块的性质。
奇怪的是,这段代码对于一个“游戏”来说完全正常,但是一旦应用程序在同一次运行中启动新的“游戏”,代码就开始产生更多的“完成”命中,我每开始一个游戏,我的动画就会有更多的“完成”命中。感觉像是一些残留垃圾互相干扰,但静态分析器中看不到任何泄漏。我感到困惑。
更新和解决方案
这个问题是由于旧的废弃游戏版仍在监听移动棋子的通知而引起的。即使旧版被释放了,当游戏结束时它们没有明确地删除通知请求。由于系统不会立即丢弃已释放的对象,因此这些“幽灵”版仍在监听全局通知以移动棋子。即使它们对我们来说已经“死了”,但它们足够活跃,可以响应并争夺棋子的动画!
解决方案是在我们释放之前让每个版
[[NSNotificationCenter defaultCenter] removeObserver:self]
。
performSelector
文档中看到的内容来看,它相当于调用选择器,因此不会改变情况。 - EFC