NSNotification observed by released objects

See Update below ... although this was originally an animation issue, it turned out to be a notification issue. Beware: NSNotification will be observed even from objects that you have dropped. Be sure to removeObserver: to avoid this.

This is the first time I have used block-based animation, and I start a situation where the “completion” block seems to receive a call more than once for one run of the animation block. I can’t understand why this will happen, but it seems that this happens with some sequence after my game has been working for some time. 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 initially starts with step = 1 and an array of tiles for the player’s pawn for animation. After completing each step, the method is called recursively to take the next step. When fragments end in the array, the task is executed. In the completed block, we either take the next step, or (if the animation has not been completed) just go 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 

Note that after starting the second step on line 4, the log reports the “incomplete” completion of the first step on line 6, and then the completed second step on line 7. However, step 1 was completed on line 3. How can it be completed as on line 3, so on line 6? In this case, one of them was completed with completed = YES, and the other with completed = NO, but I also saw this run with two or more completed lines = YES, registered one step.

What could cause something like that? I don’t even know where to start looking for an error, or maybe I just don’t understand the nature of the completion block for iOS animation.

It’s strange that this code works fine for one “game”, but as soon as the application launches a new “game” in the same run, the code starts creating more “final” hits, the more games I launch, the more “final” hits I get for the animation. It seems that some garbage left over from old games is interfering, but there are no leaks in the static analyzer. I am puzzled.

Update and solution

This problem was caused by the fact that the old discarded game boards were still listening to pawn movement notifications. Although the old boards were released, they did not specifically delete notification requests when games ended. Since the released objects are not immediately discarded by the system, these “ghost” boards still listened to the global notification for moving pawns. And although they were “dead” to us, they were alive enough to react and fight for the animation of the pawns!

The solution should be that each board [[NSNotificationCenter defaultCenter] removeObserver:self] released.

+4
source share
2 answers

See update to the question. This issue turned out to be related to notifications, which at first called on the pawns to move, calling from the old issued copies of our advice. Beware that notifications exit the release of objects, make sure that you explicitly delete notification requests when you are finished with objects!

+1
source

I'm not sure, but I think this is due to the fact that you change the position in the same runloop as the animation, so interrupt the first animation. If you have done this:

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

I think this will fix your problem.

0
source

Source: https://habr.com/ru/post/1388702/


All Articles