NSTimer,Threads。如何创建线程NSTimer?

时间:2012-02-28 18:15:38

标签: objective-c nstimer nsthread

我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须使用检索到的结果更新UITableView。

为此,我计划使用NSTimer,每隔10秒运行一次访问数据库和检索数据的更新过程。

问题是如果我在主线程中运行它,视图将被冻结,直到检索并加载数据。

任何人都知道这是最好的方法吗?任何一个例子都是apreciated。

感谢。

编辑----

这是我用来启动的代码&停止更新过程:

-(void)startUpdating
{
    self.isUpdating = YES;

    timerLoop = [[NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(listMustBeUpdated:) userInfo:nil repeats:YES] retain];

    [[NSRunLoop mainRunLoop] addTimer: timerLoop
                              forMode: UITrackingRunLoopMode];


}
-(void)stopUpdating
{
    self.isUpdating = NO;

    [timerLoop invalidate];
    [timerLoop release];
    timerLoop = nil;
}

此外,几分钟后运行app chashes而没有错误消息。我认为这是因为内存使用或僵尸,但我已经用仪器测试,内存使用量为3.11 Mb(live)&没有僵尸。

也许是因为计时器?

感谢。

编辑--- 2

好的,现在这是我用于整个问题的代码,并且在滚动时仍然会冻结UITableView。

也许是因为从线程更新并且从主线程执行滚动?

-(void)loadMessagesFromUser:(int)userId toUserId:(int)userToId
{
    fromUserId = userId;
    toUserId = userToId;

    messages = [OTChatDataManager readMessagesFromUser:userId toUser:userToId];

    lastMessageId = [[messages getValueByName:@"Id" posY:[messages CountY]-1] intValue];

    [list reloadData];
}
-(void)messagesMustBeUpdated:(id)sender
{
    if ([NSThread isMainThread]) {
        [self performSelectorInBackground:@selector(updateMessages:) withObject:nil];
        return;
    }


}
-(void)updateMessages:(id)sender
{
    iSQLResult *newMessages = [OTChatDataManager readMessagesFromUser:fromUserId toUser:toUserId sinceLastMessageId:lastMessageId];

    for (int a=0;a<[newMessages CountY];a++)
    {
        [messages.Records addObject:[newMessages.Records objectAtIndex:a]];
        messages.CountY++;
    }

    lastMessageId = [[messages getValueByName:@"Id" posY:[messages CountY]-1] intValue];

    [list reloadData];

}
-(void)startUpdating
{
    self.isUpdating = YES;

    timerLoop = [[NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(messagesMustBeUpdated:) userInfo:nil repeats:YES] retain];

}
-(void)stopUpdating
{
    self.isUpdating = NO;

    [timerLoop invalidate];
    [timerLoop release];
    timerLoop = nil;
}

可以从mainThread调用loadMessagesFromUser并访问相同的非原子对象。安静的崩溃是否可能因为这个?

从主线程调用

启动和停止函数。其余的是计时器的东西。

感谢您的回复。

3 个答案:

答案 0 :(得分:1)

启动计时器调用的方法:

if ([NSThread isMainThread]) {
    // Must NOT be main thread
    [self performSelectorInBackground:@selector(listMustBeUpdated:) withObject:nil];
    return;
}

我没有注意到您发布的代码中有任何内容会导致崩溃但没有输出。我想象它在其他代码中。如果你想问这个问题,我建议发布另一个问题,提供更多关于你究竟在做什么的信息。

答案 1 :(得分:1)

首先要考虑的是如何访问远程数据库。如果您只是通过HTTP获取URL的内容,则可以使用NSURLConnection异步获取内容。您可以将其设置为使用委托或完成块。它将执行HTTP请求并在响应到达时通知您,并且您不必担心线程。

如果您使用某个库来访问远程数据库,并且该库尚未提供异步API,那么您需要担心阻塞主线程。您可以使用Grand Central Dispatch(GCD)来避免阻塞主线程。

在视图控制器中,您需要一个GCD队列,一个NSTimer和一个标志,以确保如果一个数据库请求已在运行且需要很长时间,则不会启动第二个数据库请求。 / p>

@implementation ViewController {
    dispatch_queue_t _databaseQueue;
    NSTimer *_databaseTimer;
    BOOL _databaseQueryIsRunning;
}

您需要清除dealloc中的GCD队列和计时器。

- (void)dealloc {
    if (_databaseQueue)
        dispatch_release(_databaseQueue);
    [_databaseTimer invalidate];
    [_databaseTimer release];
    [super dealloc];
}

您可以在视图出现时启动计时器。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    _databaseTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self
        selector:@selector(databaseTimerDidFire:) userInfo:nil repeats:YES] retain];
}

当视图消失时,您可以停止计时器。

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [_databaseTimer invalidate];
    [_databaseTimer release];
    _databaseTimer = nil;
}

当计时器触发时,如果没有正在运行的数据库请求,则在GCD队列上启动数据库请求。

- (void)databaseTimerDidFire:(NSTimer *)timer {
    if (_databaseQueryIsRunning)
        return;
    _databaseQueryIsRunning = YES;

    // Lazily create the GCD queue.
    if (!_databaseQueue)
        _databaseQueue = dispatch_queue_create("MyDatabaseQueue", 0);

    dispatch_async(_databaseQueue, ^{
        // This block runs off of the main thread.
        NSObject *response = [self performDatabaseRequest];

        // UI updates must happen on the main thread.
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self updateViewWithResponse:response];
        });
        _databaseQueryIsRunning = NO;
    });
}

答案 2 :(得分:0)

我在this博客中找到了解决方案。

@interface Test : NSObject
{
     NSTimer *_timer;
     NSRunLoop *_runLoop;
     NSThread *_timerThread;
}

- (void)suspendTimer;
- (void)resumeTimer;
- (void)timerFireMethod:(NSTimer*)timer;

@end // End Test interface


@implementation Test

- (void)suspendTimer
{
     if(_timer != nil)
     { 
          [_timer invalidate];
          [_timer release];
          _timer = nil;

          CFRunLoopStop([_runLoop getCFRunLoop]);

          [_timerThread release];
          _timerThread = nil;
     }
}

- (void)_startTimerThread
{
     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

     _runLoop = [NSRunLoop currentRunLoop];
     _timer = [[NSTimer scheduledTimerWithTimeInterval:1.0
          target:self
          selector:@selector(timerFireMethod:)
          userInfo:nil
          repeats:YES] retain];
     [_runLoop run];
     [pool release];
}

- (void)resumeTimer
{
     if(_timer == nil)
     {
          _timerThread = [[NSThread alloc] initWithTarget:self
               selector:@selector(_startTimerThread)
               object:nil];
          [_timerThread start];
     }
}

- (void)timerFireMethod:(NSTimer*)timer
{
     // ...
}

@end // End Test implementation

我也在使用它,它对我来说很好。