NSTimer, Темы.Как создать резьбовой NSTimer? - PullRequest
0 голосов
/ 28 февраля 2012

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

Для этого я планирую использовать NSTimer, который каждые 10 секунд запускает процесс обновления, при котором осуществляется доступ к базе данных и извлечение данных.

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

Кто-нибудь знает, какой лучший способ сделать это? Любой пример будет оценен.

Спасибо.

РЕДАКТИРОВАТЬ ----

Это код, который я использую для запуска и остановки процесса обновления:

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

Кроме того, через несколько минут приложение работает без сообщения об ошибке. Я думаю, это потому, что использование памяти или зомби, но я проверил с инструментами, и использование памяти было 3,11 Мб (в реальном времени) и без зомби.

Может, потому что таймер?

Спасибо.

РЕДАКТИРОВАТЬ --- 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;
}

loadMessagesFromUser может вызываться из mainThread и обращаться к одному и тому же неатомическому объекту. Возможно ли тихое падение из-за этого?

функции запуска и остановки вызываются из основного потока. Остальное - это таймер.

Спасибо за ответы.

Ответы [ 3 ]

1 голос
/ 28 февраля 2012

Первое, что нужно рассмотреть, - это как вы обращаетесь к удаленной базе данных.Если вы просто извлекаете содержимое URL по HTTP, вы можете использовать NSURLConnection для асинхронного извлечения содержимого.Вы можете настроить его на использование делегата или блока завершения.Он выполнит HTTP-запрос и уведомит вас о получении ответа, и вам не нужно беспокоиться о потоках.

Если вы используете какую-то библиотеку для доступа к удаленной базе данных, а библиотека неуже предоставляют асинхронный API, тогда вам нужно беспокоиться о блокировке основного потока.Вы можете использовать Grand Central Dispatch (GCD), чтобы избежать блокировки основного потока.

В вашем контроллере представления вам понадобится очередь GCD, NSTimer и флаг, чтобы убедиться, что вы этого не делаетезапустите второй запрос к базе данных, если он уже запущен и занимает много времени.

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

Вам необходимо очистить очередь GCD и таймер в вашем dealloc.

- (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;
    });
}
1 голос
/ 28 февраля 2012

Запустите метод, который вызывает ваш таймер, с помощью:

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

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

0 голосов
/ 28 февраля 2012

Я нашел решение в этом блоге.

@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

Я также использую это, и он отлично работает для меня.

...