iPhone EXC_BAD_ACCESS при вызове [super dealloc] на пользовательском UIViewController - PullRequest
3 голосов
/ 06 августа 2010

Я в растерянности! Это одна из тех неприятных ошибок, которые случаются только при определенных условиях, но я не могу напрямую связать условия и результаты.

Мое приложение имеет постраничный UIScrollView, где представление каждой страницы происходит из MyViewController, подкласса UITableViewController. Чтобы минимизировать использование памяти, я выгружаю те контроллеры, которые в данный момент не видны. Вот мой метод «очистки»:

- (void) cleanViewControllers:(BOOL)all {

    if (all) { 
    // called if some major changes occurred and ALL controllers need to be cleared

        for (NSInteger i = 0; i < [viewControllers count]; i++)
            [viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
    }
    else if ([viewControllers count] > 2) {
    // called if only the nearest, no longer visible controller need to be cleared
        NSInteger i = pageControl.currentPage - 2;
        if (i > -1) [viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
        i = pageControl.currentPage + 2;
        if (i < [viewControllers count]) [viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
    }
}

Эта строка вызывает сбой приложения:

viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];

viewControllers - это NSMutableArray, содержащий объекты типа MyViewController. MyViewController не имеет пользовательских свойств, а его метод dealloc не содержит ничего, кроме вызова [super dealloc].

Вот что показывает отладчик: альтернативный текст http://a.imageshack.us/img831/3610/screenshot20100806at126.png

Дело в том, что это происходит не каждый раз, когда контроллер очищается, а только иногда. В частности, после того, как определенные изменения инициируют полную очистку и перерисовку ScrollView, он отображает текущую страницу (назовите ее X) в порядке, но как только я прокручиваю достаточно далеко, чтобы вызвать очистку X, происходит сбой. Это сводит меня с ума!

Другое дело, что это не происходит ни в симуляторе 4.0, ни на iPad, но происходит очень стабильно на iPod touch 1-го поколения с 3.1.3.

Ответы [ 4 ]

6 голосов
/ 06 августа 2010

Что-то высвобождается, но указатель все еще висит. Установите NSZombieEnabled в YES и запустите его снова. Вот как это сделать:

  • Продукт -> Редактировать схему
  • Выберите вкладку «Аргументы»
  • Добавить в "Переменные для установки в среде"
    • Имя: NSZombieEnabled
    • Значение: ДА

В Xcode 4.1 и выше:

  • Продукт -> Редактировать схему
  • Выберите вкладку «Диагностика»
    • У вас есть возможность включить объекты зомби.

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

Счастливая охота на зомби.

1 голос
/ 26 мая 2011

Часто это может быть вызвано выделением чего-либо, установкой чего-либо внутри ViewController и последующим его освобождением, например:

UIBarButtonItem* timeLabel = [[UIBarButtonItem alloc] initWithTitle:@"time" style:UIBarButtonItemStylePlain target:nil action:nil];

NSArray *items = [NSArray arrayWithObjects: timeLabel, nil];

self.toolbarItems = items;

Теперь естественным шагом после этого является:*

Но это вызовет EXC_BAD_ACCESS на [super dealloc], предположительно, потому что контроллер представления освобождает массив И все элементы в нем.

0 голосов
/ 06 августа 2010

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

Представление контроллера представления все еще находится в иерархии представления? Вы можете проверить что-то вроде [viewController isViewLoaded] && viewController.view.superview. Если это так, то, вероятно, небезопасно удалять контроллер представления.

(Обратите внимание на проверку isViewLoaded, поскольку UIViewController.view загрузит представление, если оно еще не загружено.)

0 голосов
/ 06 августа 2010

Вместо того, чтобы заменять элементы в массиве, вы можете просто вызвать removeObjectAtIndex: или removeAllObjects:, чтобы убедиться, что ничто не содержит ссылки там, где это не должно.

...