Когда NSThread возвращается к освобожденному объекту? (IPhone) - PullRequest
1 голос
/ 12 февраля 2010

У меня есть ошибка памяти, которая, кажется, сводится к тому, что происходит в потоке. У меня проблемы с устранением неполадок.

У меня есть UIViewController, который, когда active , т. Е. Пользователь использует его представление, получает обновления из веб-службы в NSThread.

Это делается каждые 3 минуты, и эта задержка контролируется:

[self performSelector:@selector(timerDone) withObject:nil afterDelay:180.0];

Метод timerDone теперь запускает NSThread, который извлекает данные веб-службы, а также снова отправляет сообщение performSelector. Это небольшая «проверка обновлений, заполнение представлений, завершение работы и повторение», которая работает просто отлично.

Теперь пользователь, конечно, может внезапно нажать кнопку и загрузить второй UIViewController. Когда это происходит, я звоню:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timerDone) object:nil];

И сделайте мою уборку по методу dealloc.

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

Потоки выполняются так:

- (void) runTimer {

    [self performSelector:@selector(timerDone) withObject:nil afterDelay:180];
}

- (void) timerDone {

    [self performSelector:@selector(runTimer) withObject:nil afterDelay:2];
    [NSThread detachNewThreadSelector:@selector(updateAllVisibleElements) toTarget:self withObject:nil]; 

    }

- (void) updateAllVisibleElements  {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //call approiate web service
    [pool release];
}

1 Ответ

7 голосов
/ 12 февраля 2010

У вас есть две проблемы: во-первых, вы используете performSelector:withObject:afterDelay:, чтобы делать то, что NSTimer делает лучше всего (периодический обратный вызов). cancelPreviousPerformRequestsWithTarget:selector:object: может быть довольно дорогим, и из-за ваших потоков, вероятно, создаст условия гонки.

Вторая проблема: каждый поток имеет свой собственный цикл выполнения и оба механизма (performSelector:... и NSTimer) и привязаны к циклу выполнения текущего потока.

Вот что я рекомендую: Создайте один долговечный NSThread с собственным явным циклом выполнения для всех ваших потребностей в обновлении. Посмотрите Руководство по программированию потоков , чтобы найти хороший пример кода этого. В этом потоке установите 3-х минутное повторение NSTimer. Каждые 3 минуты обновляйте.

Если вам нужно запланировать обновление вне трехминутного цикла, тогда вы используете performSelector:onThread:withObject:waitUntilDone: для вызова вашего updateAllVisibileElements. Обычно я делаю это, чтобы заключить всю логику потока в один объект (WebServiceController или любой другой). Он создает свой собственный NSThread и сохраняет его в ivar. Затем я использую такой код:

- (void)requestUpdate
{
    if ([NSThread currentThread] != self.thread)
    {
        [self performSelector:@selector(update) onThread:self.thread withObject:nil waitUntilDone:NO];
        return;
    }
    else
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        //call approiate web service
        [pool drain];
    }
}

Еще одно замечание: вы упоминаете, что фоновый поток «заполняет представления». Фоновый поток никогда не должен вызывать UIKit. UIKit не является потокобезопасным и должен вызываться только в основном потоке. Обычно я достигаю этого, публикуя уведомления в основном потоке, который наблюдают контроллеры представления. «Обновляющий» объект не должен ничего знать об интерфейсе пользователя. Это нарушает парадигму Модель-Представление-Контроллер Какао.

...