已释放对象观察到的 NSNotification
请参阅下面的更新...虽然这最初似乎是动画问题,但事实证明这是通知问题。请注意:即使您已丢弃的对象也会观察到 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];
}
}];
}
}
这是一个递归方法,最初使用 step = 1 启动,并有一个“图块”数组供玩家的 pawn 进行动画处理。每个步骤完成后,都会递归调用该方法以执行下一步。当阵列用完图块时,工作就完成了。在完成的块中,我们要么采取下一步,要么(如果动画未完成)直接跳到最后一个图块。以下是一次运行的日志...
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 行完成第二步。但是,步骤1是在第3行完成的,怎么可以在第3行和第6行都完成呢?在这种情况下,一个是用finished = YES完成的,另一个是用finished = NO完成的,但我也看到这个运行在一个步骤中记录了两个或多个finished = YES行。
什么会导致这样的事情呢?我什至不知道从哪里开始寻找错误,或者也许我只是不理解 iOS 动画完成块的本质。
奇怪的是,这段代码对于一个“游戏”来说完美地工作,但是一旦应用程序在同一次运行中开始一个新的“游戏”,代码就开始产生更多的“完成”点击,我开始的游戏越多,就越多我在每个动画中获得的“完成”点击数。感觉像是旧游戏留下的一些垃圾正在干扰,但静态分析器中没有可见泄漏。我很困惑。
更新及解决方案
这个问题是由于旧的废弃游戏板仍在监听移动棋子的通知。尽管旧版已经发布,但他们并没有专门删除比赛结束时的通知请求。由于释放的对象不会立即被系统丢弃,因此那些“幽灵”板仍在监听全局通知以移动棋子。即使它们对我们来说已经“死了”,但它们仍然活着,可以对棋子的动画做出反应并进行战斗!
解决方案是在发布每个板之前[[NSNotificationCenter defaultCenter]removeObserver:self]
。
See Update below... though this initially appeared to be an animation issue, it turned out that it was an issue with notifications. Beware: an NSNotification
will be observed even by objects you have discarded. Be sure to removeObserver:
to avoid this.
I've been using the block-based animation for the first time and run across a situation where the "completion" block seems to be getting called more than once for one firing of an "animations" block. I can't understand why this would happen, but it seems to be happening with some consistency after my game runs for a while. Here is the code in question...
- (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];
}
}];
}
}
This is a recursive method that is initially launched with step = 1 and an array of "tiles" for the player's pawn to animate across. After each step is completed, the method is called recursively to take the next step. When the array runs out of tiles, the job is done. In the completed block we either take the next step or (if the animation was not finished) just jump to the last tile. Here is a log of one run through...
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
Notice that after starting the second step on line 4 the log reports both an "unfinished" completion of the first step on line 6 and then a finished second step on line 7. However, step 1 was completed on line 3. How can it be completed on both line 3 and line 6? In this case one was completed with finished = YES and the other with finished = NO, but I have also seen this run with two or more finished = YES lines logged for a single step.
What would cause something like this? I am not even sure where to begin looking for the bug, or perhaps I just don't understand the nature of the completion block for iOS animation.
The odd thing is that this code works perfectly for one "game", but as soon as the app starts a new "game" on the same run, the code starts producing more "completion" hits, the more games I start, the more "completion" hits I get per animation. It feels like some garbage left over from old games is interfering, but no leaks are visible in the static analyzer. I'm mystified.
Update and Solution
This problem was due to the fact that the old discarded game boards were still listening to notifications to move the pawns. Even though the old boards were released, they did not specifically remove the notification requests when the games ended. Since released objects are not immediately discarded by the system, those "ghost" boards were still listening for the global notification to move the pawns. And even though they were "dead" to us, they were alive enough to respond and fight over the animation of the pawns!
The solution was to have each board [[NSNotificationCenter defaultCenter] removeObserver:self]
before we release it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
请参阅问题的更新。这个问题最终涉及到第一次调用棋子移动的通知,这些通知是从我们的棋盘视图的旧发布副本中调用的。请注意,通知在对象释放后仍然存在,请确保在使用完对象后显式删除通知请求!
See the update to the question. This issue ended up being about the notifications that first called on the pawns to move being called from old released copies of our board view. Beware, notifications survive the release of objects, make sure to explicitly remove the notification requests when you are done with objects!
我不确定,但我认为这是因为您在动画完成时更改了同一运行循环中的位置,因此中断了第一个动画。如果您这样做:
我认为这会解决您的问题。
I'm not sure but I think this is due to the fact that you're changing the position within the same runloop as the animation is finishing, therefore interrupting the first animation. If you did this:
i think it would fix your issue.