ARC: получение EXC_BAD_ACCESS из внутреннего блока, используемого в методе делегата - PullRequest
2 голосов
/ 13 сентября 2011

Я, должно быть, делаю что-то не так, но документы по автоматическому подсчету ссылок не дают мне подсказки о том, что это может быть. То, что я делаю, вызывает метод с обратным вызовом блока из метода делегата. Доступ к тому же делегату из блока приводит к неправильному доступу . Проблема в том, что объект, который я передаю - loginController , который отправляет сообщение его делегату, - явно не освобождается, когда я не обращаюсь к нему внутри блока, я могу вызвать метод несколько раз без вопрос. Вот мой код:

- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
    NSString *user = loginController.usernameLabel.text;
    NSString *pass = loginController.passwordLabel.text;

    __block MyLoginViewController *theController = loginController;
    [self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
        DLog(@"error: %@", errorMessage);
        DLog(@"View Controller: %@", theController);    // omit this: all good
        theController = nil;
    }];
}

NSZombieEnabled ничего не записывает в журнал, и от gdb нет трассировки пригодного для использования стека. Что я здесь не так делаю? Спасибо за любые указатели!


Edit:

Я полагал, что проблема имеет большую область действия - обратный вызов выше вызывается из метода NSURLConnectionDelegate (сам блок является сильным свойством для этого делегата, поэтому ARC должен вызвать Block_copy ()). Нужно ли проводить специальные измерения в этом сценарии?

Поток (loginController остается видимым все время):

LoginController

[delegate loginViewDidSubmit:self];

Просмотр делегата

(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
    [[NSURLConnection alloc] initWithRequest:req
                                    delegate:httpDelegate
                            startImmediately:YES];

NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)aConnection
  didFailWithError:(NSError *)error
{
    if (NULL != currentCallback) {
        currentCallback([error localizedDescription]);
        self.currentCallback = NULL;
    }
}

И здесь я получаю неправильный доступ, но ТОЛЬКО если получаю доступ к этой переменной loginController ...

Ответы [ 3 ]

3 голосов
/ 15 сентября 2011

Установить атрибут copy для свойства или просто вызвать метод copy для блока.

- (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
{
    callback = [callback copy];
1 голос
/ 13 сентября 2011

Фактическое решение состояло в том, что у меня был блок как strong свойство, но это должно было быть copy свойство! D'ой!


Первое «решение»:

Я только что нашел способ предотвратить плохой доступ. Как показано в моем редактировании выше, View Delegate перенаправляет блок в httpDelegate (экземпляр другого класса), который, в свою очередь, сохраняет строгую ссылку на блок. Присвоение блока временной переменной и пересылка временной переменной блока решает проблему по любой причине. Итак:

Это происходит сбой при выполнении блока, как описано

httpDelegate.currentCallback = callback;

Это работает

MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;

Я приму это как ответ, если у кого-то есть больше идей, я рад пересмотреть свое решение. :)

0 голосов
/ 13 сентября 2011

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

  1. Блок не сохраняет объект loginController (модификатор типа __block). Если блок выполняется асинхронно, loginController может перестать быть доступным, если он был убит в другом месте. Таким образом, независимо от того, что вы хотите с ним сделать, вы не сможете получить к нему доступ внутри блока, и приложение вылетит. Это может произойти, если контроллер уничтожен после отправки loginViewDidSubmit.

  2. Я думаю, что, скорее всего, это может быть ваша ситуация: loginController вызывает свой объект делегата. Метод делегата завершается синхронно, вызывая блок обратного вызова, который убивает контроллер. Ожидается, что контроллер будет активен после вызова метода делегата. Убивая его внутри метода делегата, скорее всего, произойдет сбой. Чтобы убедиться, что это проблема, просто обнулите loginController в методе делегата и поместите оператор NSLog в контроллер после вызова делегата, не говоря уже о блоке, вы получите сбой там.

Возможно, если вы вставите некоторый код, мы могли бы помочь больше.

Мой лучший.

...