Search code examples
iphoneobjective-cmultithreadingcocoa-touchios4

NSTimer scheduleTimerWithTimeInterval: and performSelectorOnMainThread


In my iPhone app I have code like :

[NSTimer scheduledTimerWithTimeInterval:03.45  
                                 target:self 
                               selector:@selector(animatePickerTimer:) 
                                userInfo:picker 
                                 repeats:NO];  

[NSTimer scheduledTimerWithTimeInterval:04.15 
                                 target:self 
                               selector:@selector(animatePickerTimer1:) 
                               userInfo:picker 
                                repeats:NO];  

[NSTimer scheduledTimerWithTimeInterval:04.91 
                                 target:self 
                               selector:@selector(animatePickerTimer2:) 
                               userInfo:picker 
                                repeats:NO]; 


-(void) animatePickerTimer: (NSTimer*) timer{
       [self performSelectorOnMainThread:@selector(animatePicker:) 
                              withObject:(UIPickerView *)timer.userInfo 
                           waitUntilDone:NO];
}

-(void) animatePickerTimer1: (NSTimer*) timer{
       [self performSelectorOnMainThread:@selector(animatePicker1:) 
                              withObject:(UIPickerView *)timer.userInfo 
                           waitUntilDone:NO];
}

-(void) animatePickerTime2r: (NSTimer*) timer{
       [self performSelectorOnMainThread:@selector(animatePicker2:) 
                              withObject:(UIPickerView *)timer.userInfo 
                           waitUntilDone:NO];
}

Threads are called after some interval of time. I want to call a method after completion of all three threads. How can I know if threads are completed?


Solution

  • There are really two ways to do this:

    1. Have each callback method call a second helper method that simply counts the number of times it's been called. Once the number of calls to that method is equal to the number of timers you have, you're done.
    2. Have a second callback, but instead of simply counting timers, store each NSTimer instance and have the helper method check isValid on each timer. Once all the timers are invalid (i.e. are no longer capable of firing), you're done.

    In code, these methods might look something like:

    NSInteger timersReceived = 0;
    
    NSTimer * timer1 = [NSTimer scheduledTimerWithTimeInterval:03.45 target:self selector:@selector(animatePickerTimer:) userInfo:picker repeats:NO];  
    NSTimer * timer2 = [NSTimer scheduledTimerWithTimeInterval:04.15 target:self selector:@selector(animatePickerTimer1:) userInfo:picker repeats:NO];  
    NSTimer * timer3 = [NSTimer scheduledTimerWithTimeInterval:04.91 target:self selector:@selector(animatePickerTimer2:) userInfo:picker repeats:NO]; 
    
    
    -(void) animatePickerTimer: (NSTimer*) timer{
           [self performSelectorOnMainThread:@selector(animatePicker:) withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           [self receivedTimer:timer];
    }
    
    -(void) animatePickerTimer1: (NSTimer*) timer{
           [self performSelectorOnMainThread:@selector(animatePicker1:) withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           [self receivedTimer:timer];
    }
    
    -(void) animatePickerTime2r: (NSTimer*) timer{
           [self performSelectorOnMainThread:@selector(animatePicker2:) withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           [self receivedTimer:timer];
    }
    
    // Method 1
    -(void) receivedTimer: (NSTimer*) timer{
           timersReceived++;
           if(timersReceived == 3) {
                  [self callMethodAfterCompletionOfAllTimers];
           }
    }
    
    // Method 2
    - (void) receivedTimer: (NSTimer*) timer{
           if(![timer1 isValid] && ![timer2 isValid] && ![timer3 isValid]) {
                  [self callMethodAfterCompletionOfAllTimers];
           }
    }

    Note that if you're using method 2, you can actually collapse all these down to a single method that you call from all three timers, since you hold on to the timer handles:

    NSTimer * timer1 = [NSTimer scheduledTimerWithTimeInterval:03.45 target:self selector:@selector(receivedTimer:) userInfo:picker repeats:NO];  
    NSTimer * timer2 = [NSTimer scheduledTimerWithTimeInterval:04.15 target:self selector:@selector(receivedTimer:) userInfo:picker repeats:NO];  
    NSTimer * timer3 = [NSTimer scheduledTimerWithTimeInterval:04.91 target:self selector:@selector(receivedTimer:) userInfo:picker repeats:NO]; 
    
    - (void)receivedTimer:(NSTimer *)timer {
           if(timer == timer1) {
                  [self performSelectorOnMainThread:@selector(animatePicker:) withObject:withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           } else if(timer == timer2) {
                  [self performSelectorOnMainThread:@selector(animatePicker1:) withObject:withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           } else if(timer == timer3) {
                  [self performSelectorOnMainThread:@selector(animatePicker2:) withObject:withObject:(UIPickerView *)timer.userInfo waitUntilDone:NO];
           }
    
           if(![timer1 isValid] && ![timer2 isValid] && ![timer3 isValid]) {
                  [self callMethodAfterCompletionOfAllTimers];
           }
    }

    This combined method is much more in line with the kind of strategy Apple uses for (e.g.) key-value observations and certain delegate protocols.