NSTimer作为超时机制

发布于 2024-09-04 12:17:48 字数 1242 浏览 1 评论 0原文

我很确定这真的很简单,我只是错过了一些明显的东西。我有一个应用程序需要从 Web 服务下载数据以在 UITableView 中显示,并且如果操作需要超过 X 秒才能完成,我想显示 UIAlertView。所以这就是我所得到的(为了简洁而简化):

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],它就会发生。

I'm pretty sure this is really simple, and I'm just missing something obvious. I have an app that needs to download data from a web service for display in a UITableView, and I want to display a UIAlertView if the operation takes more than X seconds to complete. So this is what I've got (simplified for brevity):

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
}

My problem is that I'm expecting the [self doSomethingThatTakesALongTime] call to block while the timer keeps counting, and I'm thinking that if it finishes before the timer is done counting down, it will return control of the thread to viewDidLoad where [timer invalidate] will proceed to cancel the timer. Obviously my understanding of how timers/threads work is flawed here because the way the code is written, the timer never goes off. However, if I remove the [timer invalidate], it does.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不知在何时 2024-09-11 12:17:48

我认为调度计时器并在同一线程上执行阻塞调用存在问题。在阻塞调用完成之前,运行循环无法触发计时器。

我建议您分离一个线程来执行长操作。一旦长操作完成,回调主线程以使计时器失效。

注意:使计划的同一线程上的计时器无效非常重要。

- (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
}

I think there is a problem with scheduling a timer and doing a blocking call on the same thread. Until the blocking call is completed, the run-loop cannot fire the timer.

I suggest you to detach a thread to perform the long operation. Once the long operation is finished, call back on the main thread to invalidate the timer.

Note: it is important to invalidate the timer on the same thread it was scheduled.

- (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
}
战皆罪 2024-09-11 12:17:48

您是否尝试过使用 [NSThread sleepforTimeInterval:30];

Have you tried to use [NSThread sleepforTimeInterval:30]; ?

神经暖 2024-09-11 12:17:48

sleep() 发生在主线程上,关联的运行循环永远没有机会调用计时器的选择器。

如果您在 -doSomething 中进行实际工作,并且不会阻塞线程,例如对 Web 服务的非阻塞调用,那么它将按预期工作。然而,阻塞调用必须在不同的线程中完成,这样主运行循环就不会被阻塞。

The sleep() occurs on the main thread and the associated run loop never has the chance to invoke the selector for the timer.

If you would do real work in -doSomething that doesn't block the thread, e.g. non-blocking calls to web-services, it would work as expected. Blocking calls however would have to be done in a different thread so the main run loop does not get blocked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文