问题
I'm creating a simple game.
I have the following code:
- (void)doStuff
{
double delayInSeconds = [NSNumber randomFloatBetweenLowerBound:0.8f upperBound:2.6f]; // Own category on NSNumber returns random float.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//
// Do stuff
//
if ([self shouldDoMoreStuff]) {
[self doStuff];
}
});
}
This method is run in viewDidLoad
, but it is also run when the player dies (after pressing 'Try Again').
After several deaths, eventually there are many iterations of this method being run simultaneously, rather than just one.
I don't know much about GCD and NSOperation
etc, but I'm pretty sure I should be using something to control this - perhaps NSOperation
- so in viewDidLoad
it is run, then when the player dies, I cancel the operation, and restart it.
As I say, I don't know much about NSOperation
so I don't know if it's:
a) What I should be using
and b) How I should be using it.
Can anyone provide some thoughts on how to achieve this?
Thanks as always.
回答1:
i modified the code so that you don't need block removal and adding etc.
In .h file of your viewController add
@property NSInteger currentCount;
And for your understanding and demonstration, lets assume we retry it when a touch happens on viewControllers view.
In .m file
- (void)viewDidLoad
{
[super viewDidLoad];
_currentCount=0;
[self doStuff:_currentCount];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//player dead, now start it again
_currentCount++;
[self doStuff:_currentCount];
}
- (void)doStuff:(NSInteger)count
{
NSLog(@"came top %d",count);
double delayInSeconds = 2;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(count==self.currentCount){
//
// Do stuff
//
if([self shouldDoMoreStuff]) {
[self doStuff:count];
}
}
});
}
-(BOOL)shouldDoMoreStuff{
return YES;
}
回答2:
Since you're doing this all on a single thread (the main queue is on the main thread), a simple flag should suffice. Check it before enqueuing the Block; if it's off, set it and enqueue, otherwise skip the enqueuing. The last thing the Block should do is turn off the flag.
If multiple threads were involved, you'd need something designed for inter-thread flagging, like a dispatch semaphore, to indicate that a Block had already been enqueued.
来源:https://stackoverflow.com/questions/21664412/looping-dispatch-after-in-a-method-causes-many-simultaneous-dispatches-when-meth