iPhone: использование dispatch_after для имитации NSTimer - PullRequest
2 голосов
/ 17 января 2011

Не много знаю о блоках. Как бы вы подражали повторяющимся NSTimer с dispatch_after()? Моя проблема в том, что я хочу «приостановить» таймер, когда приложение переходит в фоновый режим, но подклассы NSTimer, похоже, не работают.

Я попробовал что-то, что, кажется, работает. Я не могу судить о последствиях его работы или о том, можно ли его значительно оптимизировать. Любые входные данные приветствуются.

#import "TimerWithPause.h"


@implementation TimerWithPause

@synthesize timeInterval;
@synthesize userInfo;
@synthesize invalid;
@synthesize invocation;

+ (TimerWithPause *)scheduledTimerWithTimeInterval:(NSTimeInterval)aTimeInterval target:(id)aTarget selector:(SEL)aSelector userInfo:(id)aUserInfo repeats:(BOOL)aTimerRepeats {

    TimerWithPause *timer = [[[TimerWithPause alloc] init] autorelease];

    timer.timeInterval  = aTimeInterval;

    NSMethodSignature *signature = [[aTarget class] instanceMethodSignatureForSelector:aSelector];
    NSInvocation *aInvocation = [NSInvocation invocationWithMethodSignature:signature];
    [aInvocation setSelector:aSelector];
    [aInvocation setTarget:aTarget];
    [aInvocation setArgument:&timer atIndex:2];
    timer.invocation = aInvocation;

    timer.userInfo      = aUserInfo;

    if (!aTimerRepeats) {
        timer.invalid = YES;
    }

    [timer fireAfterDelay];

    return timer;
}

- (void)fireAfterDelay {

    dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, self.timeInterval * NSEC_PER_SEC);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_after(delay, queue, ^{

        [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:NO];

        if (!invalid) {         
            [self fireAfterDelay];
        }
    });
}

- (void)invalidate {
    invalid = YES;

    [invocation release];
    invocation = nil;

    [userInfo release];
    userInfo = nil;
}

- (void)dealloc {

    [self invalidate];

    [super dealloc];
}

@end

Ответы [ 3 ]

5 голосов
/ 26 апреля 2011

Так что для таймеров, которые вы хотите запустить блок, вы бы источник отправки. На сайте ADC есть отличный пример, который я использовал много раз вместо NSTimer в своих приложениях:

http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html%23//apple_ref/doc/uid/TP40008091-CH103-SW2

4 голосов
/ 17 января 2011

Вы также можете просто "приостановить" ваниль NSTimer, установив для fireDate значение [NSDate distantFuture]. Когда ваше приложение вернется на передний план, вы сможете просто возобновить нормальное расписание.

3 голосов
/ 17 января 2011

Я сам немного знаю о блоках и фоновых задачах, но я бы подумал о другой технике, чтобы «приостановить» ваш таймер:

Я бы предложил перезаписать applicationDidEnterBackground и сохранить оставшееся время для таймера (на основе NSTimer's fireDate), а затем, когда приложение вернулось на передний план и applicationWillEnterForeground сработало, сделать недействительным и пересчитать ваши таймеры на основе временной метки, которую вы сохранили ранее.

Я не тестировал это решение, но, похоже, оно должно работать

Надеюсь, это поможет:)

...