Выпуск NSOperationQueue и UITableView приводит к сбою моего приложения - PullRequest
0 голосов
/ 07 сентября 2011

Это самая странная проблема, с которой я застрял.

У меня есть UIViewController в UINavigationController, и я хочу вызвать метод в viewDidAppear с помощью NSInvocationOperation, чтобы он мог работать в обратном потоке, когдавид становится видимым.

Проблема заключается в том, что если вынуть контроллер представления ДО , то операция (в данном случае метод testMethod ) завершится, приложение аварийно завершит работу.

Все работает нормально, если я выскакиваю контроллер вида ПОСЛЕ операция идет своим чередом.

Когда приложение аварийно завершает работу, оно останавливается в [super dealloc] с «EXC-BAD-ACCESS» и выдает мне следующую ошибку:

bool _WebTryThreadLock (bool), xxxxxxxxx:Попытка получить веб-блокировку из потока, отличного от основного потока или веб-потока.Это может быть результатом обращения к UIKit из вторичного потока.Сбой сейчас ...

И это мой код (супер упрощенный) ..

- (void)viewDidAppear:(BOOL)animated
{
     [super viewDidAppear:animated];

    NSInvocationOperation *theOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testMethod) object:nil];
    [operationQueue addOperation:theOperation];
    [theOperation release];
}

- (void)testMethod
{
    NSLog(@"do some stuff that takes a few seconds to complete");
}

- (void)dealloc
{
    [_tableView release];
    [super dealloc];
}

testMethod имеет некоторый код, который занимает несколько секундзавершить.У меня есть только несколько подсказок, и я действительно не знаю, как и где начать отлаживать это.

  • Подсказка # 1: Самое смешное, что если я удалю [_tableView release];от dealloc, то приложение не падает.Но, конечно, это может вызвать утечку, и я не могу ее устранить.

  • Подсказка # 2: я тестировал этот код на отдельном "чистом" UIViewController с UITableView имое удивление, что не аварийно завершился.

  • Подсказка # 3: Приложение не аварийно завершает работу, если для источника данных UITableView установлено значение nil в viewDidLoad.

  • Подсказка # 4: приложение не выглядит сбой, если я использую тот же код в viewDidAppear где-то еще, как IBAction.

  • Подсказка # 5:Я пытался просматривать данные стека с помощью NSZombie, но он дает мне тонны данных и ни к чему не приводит.

У меня есть очень сложный код в моих UITableViewDelegate и UITableViewDataSource, и я действительно не понимаюне знаю, с чего начать отладку этого.Я действительно надеюсь, что мне не придется проходить построчно или переписывать все это из-за этого.

Есть какие-нибудь указатели на то, куда я должен смотреть?

Ответы [ 2 ]

2 голосов
/ 07 сентября 2011

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

Чтобы этого не происходило, вам нужно отправить сообщение keep-alive на ваш контроллер в главном потоке в конце вашей операции, добавив что-то вроде этого в последнюю строку в вашем testMethod:

[self performSelectorOnMainThread:@selector(description) withObject:nil waitUntilDone:NO];

Существует вероятность того, что это сообщение может быть обработано до того, как очередь операций освободит ваш контроллер представления, но этот шанс довольно мал. Если это все еще происходит, вы можете сделать что-то вроде этого:

[self performSelectorOnMainThread:@selector(keepAlive:) 
                       withObject:[NSNumber numberWithBool:YES]
                    waitUntilDone:NO];

- (void)keepAlive:(NSNumber *)fromBackground
{
    if (fromBackground)
         [self performSelector:@selector(keepAlive:) withObject:nil afterDelay:1];
}

Отправляя сообщение вашему контроллеру представления в главном потоке, он будет поддерживать живой объект (NSObject сохраняет ваш контроллер представления до тех пор, пока основной поток не обработает сообщение). Он также сохранит работоспособность контроллера представления, если вы выполните селектор после задержки.

1 голос
/ 07 сентября 2011

Вы отказываете, потому что контроллер все еще пытается использовать вашу ссылку на tableView, и так как вы запустили viewController, все исчезнет в dealloc, и tableView все еще заполнится сам. Вы можете попытаться задать свой метод dealloc, если ваша операция все еще выполняется, поэтому вы можете отменить ее, и все должно быть в порядке.

После добавления операции в очередь, операция выходит из Руки. Очередь вступает во владение и обрабатывает планирование этой задачи. Однако, если вы решите позже, что вы не хотите выполнять операция в конце концов - потому что пользователь нажал кнопку отмены в например, панель прогресса или выйдите из приложения - вы можете отменить операция, чтобы предотвратить ненужное использование процессорного времени. Ты сделаешь это путем вызова метода отмены самого объекта операции или вызов метода cancelAllOperations класса NSOperationQueue.

Отмена операции не заставляет ее немедленно остановить делается. Хотя уважение к значению, возвращаемому isCancelled, ожидается от всех операций, ваш код должен явно проверить значение возвращается этим методом и прерывается по мере необходимости. По умолчанию реализация NSOperation действительно включает в себя проверки на отмену. Например, если вы отмените операцию, прежде чем ее метод запуска вызвано, метод start завершается без запуска задачи.

...