Question

I'm working on a music app, so I can't really approve any latancy.

I'm using the beloved touchesBegan/Moved/Ended to process my touches. Everything is going well, and I managed to synthesize a tone(using AudioUnit) and to show up a glow under the finger(using GLKit), and it all works fine if there's less then 4-7 notes/touches hitting at the same time, then it goes nuts and make the app stuck.

I understood it's becuase I'm doing lots of work(with the GLKit interface and the interface I made for my synth engine) and I need a way to fix it.

My code is built around like this:



- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for(UITouch *touch in touches)
    {
        CGPoint lePoint = [touch locationInView:self.view];


        doing some calculataions for synth engine.......
        actually making the sound avilable to play through the interface........

        then I start to create the sprite object for the glow
        attaching it to the nsarray of the shapes
        //rejoycing in the awesomness of the sound


        adding the touch to the active touches array


    }

}

I do the exact reverse in the touchesEnded.

So in my attempts to make it work better I tried using GCD for the GLKit stuff, so that it would happen in asynchronously, It worked, but at times I got to see a glow staying on screen because it wasn't in the array when touchesEnded tried to remove it.

So that didn't work, and I'm kind of clueless, if anybody can help I'd be thankful.

Was it helpful?

Solution

Use GCD, but make sure you use a concurrent queue so you can submit all touches simultaneously. If you need to wait for their completion, use dispatch_apply, otherwise just kick them off asynchronously.

You can not cancel a block once it is scheduled, so you may want to put a check to see if the touch has ended or canceled, and have the block return immediately.

For example, something like...

for (UITouch *touch in touches) {
    NSValue *key = [NSValue valueWithPointer:(__bridge void*)touch];
    MyData *data = [dictionary objectForKey:key];
    // set whatever attributes you want in the data for this touch
    // You may want to have a cancel flag in there, so you can set it later...
    CGPoint touchPoint = [touch locationInView:self.view];
    dispatch_async(someConcurrentQueue, ^{
        // If only one thing to do, check the flag at beginning, if it is a long task
        // may want to check it periodically so you cancel as soon as possible.
        if (data.touchHasEnded) return;
    });

An alternative is to have a queue per touch. You create the dispatch queue in touchesBegan, and just toss tasks on it. You can let them all finish, or maybe some of them should just be canceled in touchesEnd.

For example, if the state has already changed to something else, there may be no sense processing the remaining touches in-between.

I have done line-smoothing like this.

Either way, if you are doing intensive processing, you should let the system figure out how to manage it.. just make sure you can terminate what you are doing if appropriate.

Make sure you mark the state as done, and do not process anything else after touchesEnd... You are, after all, only using a pointer as the key to the dictionary...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top