简体   繁体   English

已发布对象观察到的NSNotification

[英]NSNotification observed by released objects

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. 注意:即使您丢弃的对象也会观察到NSNotification Be sure to removeObserver: to avoid this. 一定要removeObserver:以避免这种情况。

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. 这是一个递归方法,最初使用step = 1启动,并为玩家的pawn设置一个“tiles”数组以进行动画制作。 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? 请注意,在第4行开始第二步后,日志报告第6行第一步的“未完成”完成,然后第7行报告完成的第二步。但是,第3行在第3行完成。它怎么可能是3号线和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. 在这种情况下,一个完成了完成= YES而另一个完成= NO,但我也看到这个运行时记录了一个步骤的两个或多个finished = YES行。

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. 我甚至不确定从哪里开始寻找bug,或者我只是不明白iOS动画的完成块的性质。

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. 解决方案是在我们发布之前让每个板[[NSNotificationCenter defaultCenter] removeObserver:self]

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. 这个问题最终是关于第一次调用pawns从我们的董事会视图的旧发布副本调用的通知。 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. 我不确定,但我认为这是因为你在动画结束时改变了同一个runloop中的位置,因此中断了第一个动画。 If you did this: 如果你这样做:

[self performSelector:@selector(takeStep:) withObject:[object that describes step] afterDelay:0.0];

i think it would fix your issue. 我认为它会解决你的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM