Повторный вызов push для UINavigationController вызывает ошибку доступа

У меня есть приложение TabView с вкладкой, в которой есть NavView в качестве одного из представлений. Это представление имеет подпредставление с TableView для хранения событий. Я включил кнопку «Добавить» в правом верхнем углу панели навигации и назначил IBAction кнопке.

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

Это работает в первый раз, но во второй раз, когда я нажимаю кнопку «Добавить», я получаю ошибку EXC_BAD_ACCESS. Я выполнил отладку до нажатия контроллера на навигационный контроллер, и я подтвердил, что именно здесь возникает исключение. Я прочитал документы, и в нем говорится, что навигационный контроллер вытолкнет контроллер, нажав кнопку «Назад», расположенную в верхней части навигационной панели. Я также знаю, что возникнет необработанное исключение, если я попытаюсь поместить одно и то же представление в стек. Я даже пытался вернуться к root непосредственно перед нажатием, чтобы очистить стек, но все равно получаю исключение при нажатии во второй раз.

Должен ли я выталкивать это вручную (то есть навигационная кнопка «назад» на самом деле не выталкивает это) где-то еще? Я также подтвердил, что экземпляр как навигационного контроллера, так и вновь созданного экземпляра контроллера представления не пусты.

Вот фрагмент кода для кнопки «Добавить»:

- (IBAction) addEvent: (id)sender {

 // Here we'd instantiate an instance of our Add Event Controller to show the form that allows us to enter a new event.

 // We'd add the context to the class from here so that it can get to our Core Data


  EventEntryViewController *fvController = [[EventEntryViewController alloc] initWithNibName:@"AddEventView" bundle:nil];

 fvController.managedObjectContext = self.managedObjectContext;

 [self.navigationController pushViewController:fvController animated:YES];

 [fvController release];

 fvController = nil;

}

Спасибо за любую помощь.

Обновлять:

Хорошо, я предотвратил возникновение исключения. Я попробовал очень простое представление, в котором ничего не было, IBOutlets или IBActions и класс контроллера, у которого не было атрибутов. Это сработало, поэтому я решил, что это проблема с моим EventEntryViewController. Он мало что сделал, а обратная трассировка показала, что он умирает на внутренностях отображения представления, поэтому я даже не добрался до метода loadView, не говоря уже о любой другой части моего кода. У меня было несколько атрибутов класса, которые были инициализированы при предыдущей загрузке, и как добропорядочный гражданин я освободил их в viewDidUnload() и освободил их в Dealloc(). Когда я закомментировал раскладку атрибутов моего класса, это сработало!

Меня это смутило, и я поместил строки NSLog() в viewDidLoad(), viewDidUnload() и dalloc(). Вот результаты двух последовательностей push после того, как я удалил dallocs атрибутов моего класса:

2010-09-18 20:17:46.224 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:51.391 myFuel[6435:207] **EventEntryViewController**: View Did Load // loading up my class
2010-09-18 20:17:53.954 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:17:54.314 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:17:54.315 myFuel[6435:207] **EventEntryViewController**: dalloc       // after the class should have been popped
2010-09-18 20:18:02.803 myFuel[6435:207] **EventEntryViewController**: View Did Load //loading up my class
2010-09-18 20:18:08.134 myFuel[6435:207] EventTableNavViewController: viewWillAppear
2010-09-18 20:18:08.494 myFuel[6435:207] EventTableNavViewController: viewDidAppear
2010-09-18 20:18:08.495 myFuel[6435:207] **EventEntryViewController**: dalloc    // after the class should have been popped

Я что-то упустил здесь? Разве представление не должно вызывать viewDidUnload(), тем самым освобождая атрибуты класса? Я вижу, как вызывается Dealloc, но где я могу их освободить?


person mcgski    schedule 18.09.2010    source источник


Ответы (1)


Пара вещей, основанных на том, что вы сказали, хотя я не совсем понимаю, что вы делали.

Во-первых, вызов viewDidUnload: не гарантируется. Как правило, он будет вызываться только в том случае, если ваш контроллер представления выгружает его представление в ответ на предупреждение о памяти. В результате все, на что вы сохраняете ссылки в viewDidLoad: или какие-либо подключенные IBOutlets, должно быть выпущено в viewDidUnload: И в dealloc.

Во-вторых, вы говорите, что «освободили их в Dealloc» в отношении ваших «атрибутов класса», что, как я понимаю, означает, что вы фактически вызываете dealloc для других объектов. Если да... НЕ ДЕЛАЙТЕ ЭТОГО. Вы должны вызывать release только для объектов, которые вы сохранили, и редко когда-либо будет экземпляр, когда вы вызываете dealloc напрямую. Это связано с тем, что dealloc будет вызываться в реализации объекта release, когда он будет выпущен в последний раз. В этом весь смысл системы удержания/освобождения.

person imaginaryboy    schedule 19.09.2010
comment
Спасибо. Я читал об управлении памятью в Objective-C и, в частности, в среде iOS. Мне пришлось покопаться в синапсах о подсчете ссылок в старом мире MS COM, чтобы лампочка погасла. Просто для ясности: в обычных операциях вызывается Dealloc, и мой релиз (если это последняя ссылка на объект) будет подхвачен GC. При предупреждении о памяти вызывался бы viewDidUnload, и релизы позволяли бы сборщику мусора все почистить. Вопрос в том, будет ли вызываться viewDidUnload и Dealloc в этой последней ситуации, а последующий выпуск вызовет исключение? - person mcgski; 19.09.2010
comment
Когда вы выпускаете в пределах viewDidUnload:, обязательно также установите ссылку на nil. Тогда при вызове release в вашем dealloc вы не будете вызывать релиз по оборванной ссылке. Так, например, если бы у вас был IBOutlet UILabel* label, из 'viewDidUnload:` вы бы выполнили: [label release]; label = nil; и тот же код в вашем dealloc. Как вы знаете, вы можете безопасно отправить сообщение по ссылке nil, поэтому выпуск в dealloc безопасен. - person imaginaryboy; 19.09.2010