iPhone: использование dispatch_after для имитации NSTimer

Не очень разбираюсь в блоках. Как бы вы подражали повторяющемуся 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

person Joseph Tura    schedule 17.01.2011    source источник


Ответы (3)



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

person omz    schedule 17.01.2011

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

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

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

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

person user547539    schedule 17.01.2011
comment
Я попробовал это, сохранив информацию внутри userInfo. Но я не думаю, что вы можете обновить это. Хранить его вне объекта таймера не имеет особого смысла. Я мог бы обернуть таймер в другой объект, но я не думаю, что это очень элегантное решение. Может еще так... - person Joseph Tura; 17.01.2011
comment
Если вы хотите сохранить эту информацию в самом таймере, почему бы не использовать изменяемую коллекцию в качестве userInfo таймера? Оберните оставшееся время до следующего fireDate в NSNumber и сохраните его в коллекции, которая является пользовательской информацией таймера... - person danyowdee; 11.02.2011
comment
На самом деле я создал категорию, которая делает именно это: github.com/adamjernst/NSTimer-AbsoluteFireDate - person Adam Ernst; 21.10.2011