NSTimer как механизм тайм-аута - PullRequest
2 голосов
/ 03 июня 2010

Я почти уверен, что это действительно просто, и я просто упускаю что-то очевидное. У меня есть приложение, которое нуждается в загрузке данных из веб-службы для отображения в UITableView, и я хочу отобразить UIAlertView, если операция занимает более X секунд. Вот что я получил (для краткости упрощенно):

MyViewController.h

@interface MyViewController : UIViewController
        <UITableViewDelegate, UITableViewDataSource> {
    NSTimer *timer;
}

@property (nonatomic, retain) NSTimer *timer;

MyViewController.m

@implementation MyViewController

@synthesize timer;

- (void)viewDidLoad {
    timer = [NSTimer scheduledTimerWithTimeInterval:20
          target:self
        selector:@selector(initializationTimedOut:)
        userInfo:nil
         repeats:NO];

    [self doSomethingThatTakesALongTime];
    [timer invalidate];
}

- (void)doSomethingThatTakesALongTime {
    sleep(30); // for testing only
    // web service calls etc. go here
}

- (void)initializationTimedOut:(NSTimer *)theTimer {
    // show the alert view
}

Моя проблема в том, что я ожидаю, что вызов [self doSomethingThatTakesALongTime] будет блокироваться, пока таймер продолжает считать, и я думаю, что если он завершится до того, как таймер завершит обратный отсчет, он вернет управление потоком viewDidLoad, где [timer invalidate] продолжит отмену таймера. Очевидно, мое понимание того, как работают таймеры / потоки, здесь неверно, потому что при написании кода таймер никогда не выключается. Однако, если я удаляю [timer invalidate], он делает.

Ответы [ 3 ]

3 голосов
/ 03 июня 2010

Я думаю, что есть проблема с планированием таймера и выполнением блокирующего вызова в том же потоке. Пока блокирующий вызов не завершен, цикл выполнения не может запустить таймер.

Я предлагаю вам отсоединить нить для выполнения длительной операции. После завершения длительной операции перезвоните в главном потоке, чтобы отключить таймер.

Примечание: важно сделать недействительным таймер в том же потоке, на котором он был запланирован.

- (void)viewDidLoad {
    timer = [NSTimer scheduledTimerWithTimeInterval:20
          target:self
        selector:@selector(initializationTimedOut:)
        userInfo:nil
         repeats:NO];

    [NSThread detachNewThreadSelector:@selector(doSomethingThatTakesALongTime:) toTarget:self withObject:nil];
}

- (void)doSomethingThatTakesALongTime:(id)arg {
    sleep(30); // for testing only
    // web service calls etc. go here
    [self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:NO];
}

- (void)invalidate {
    [timer invalidate];
}

- (void)initializationTimedOut:(NSTimer *)theTimer {
    // show the alert view
}
0 голосов
/ 03 июня 2010

sleep() происходит в основном потоке, и связанный цикл выполнения никогда не имеет возможности вызвать селектор для таймера.

Если вы выполняете реальную работу в -doSomething, которая не блокирует поток, например неблокирующие звонки на веб-сервисы, все будет работать как положено. Однако блокирующие вызовы должны выполняться в другом потоке, чтобы основной цикл выполнения не блокировался.

0 голосов
/ 03 июня 2010

Вы пытались использовать [NSThread sleepforTimeInterval:30];?

...