#ios #memory-management #uipopovercontroller #dealloc #automatic-ref-counting
#iOS #управление памятью #uipopovercontroller #освобождение #автоматический подсчет ссылок
Вопрос:
При отображении всплывающего контроллера во второй раз (после его отклонения и последующего повторного отображения) я получаю следующую ошибку:
Завершение работы приложения из-за неперехваченного исключения ‘NSGenericException’, причина: ‘- [Освобождение UIPopoverController] достигнуто, пока всплывающее окно все еще видно.’
Трассировка стека представляет собой всего лишь набор шестнадцатеричных символов, и SIGABRT происходит в UIApplicationMain каждый раз. Вот код, который запускает кнопка:
- (IBAction)createNewScore:(id)sender {
if (self.pc)
if (self.pc.popoverVisible)
return;
else
// Breakpoint is hit here—crashes after this line
[self.pc presentPopoverFromBarButtonItem:(UIBarButtonItem *)sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
NGDocumentInfoViewController *documentInfoVC = [[NGDocumentInfoViewController alloc] initWithBlankDocumentTargetInManagedObjectContext:self.context];
UINavigationController *navc = [[UINavigationController alloc] initWithRootViewController:documentInfoVC];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneCreatingNewScore:)];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelCreatingNewScore:)];
navc.navigationBar.topItem.leftBarButtonItem = doneButton;
navc.navigationBar.topItem.rightBarButtonItem = cancelButton;
CGSize popoverSize = CGSizeMake(documentInfoVC.view.bounds.size.width, documentInfoVC.view.bounds.size.height);
documentInfoVC.contentSizeForViewInPopover = popoverSize;
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:navc];
popover.delegate = self;
self.pc = popover;
[popover presentPopoverFromBarButtonItem:(UIBarButtonItem *)sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
Я бы хотел просто сохранить всплывающее окно, которое устранило бы проблему, но это среда ARC, поэтому у меня нет сохранения. Есть ли у меня способ исправить ошибку (без отключения ARC для файла и необходимости вручную обрабатывать память для всего файла)?
Редактировать: всплывающее окно сохраняется как ivar:
@property (strong) UIPopoverController *pc;
У кого-нибудь есть решение этой проблемы (возможно, переопределение ARC)? Я подам BR, как предлагает CodaFi, но решение все равно было бы неплохо, поскольку это препятствие в крупном проекте. Если это невозможно, то, я полагаю, я сделаю свой собственный.
Комментарии:
1. Можете ли вы установить символическую точку останова
-[UIPopoverController dealloc]
(используя внизу навигатора точек останова в Xcode) и посмотреть, где она останавливается в отладчике? Возможно, трассировка стека там может дать некоторое представление о том, что освобождает ваше всплывающее окно.2. Точка останова явно не задана. Это приостанавливается в UIApplcationMain перед сбоем, что, по-видимому, указывает на то, что это, по крайней мере, частично связано с циклом выполнения. У меня возникает соблазн назвать это ошибкой с ARC, поскольку она настолько низкоуровневая. Полная трассировка стека: bit.ly/rTf7f0
3.
createNewScore
Выполняется в основном потоке?4. Да, здесь нигде не происходит явной обработки потоков. Если есть задействованные потоки, они будут в коде Apple, а не в моем.
5. Просто небольшое предупреждение, UIPopover в целом — дерьмо по сравнению с остальными реализациями ios. Отправьте свой отчет об ошибке, но из того, что я слышал от людей, которые отправились на WWDC, apple не собирается исправлять всплывающие окна в ближайшее время.
Ответ №1:
Я столкнулся с той же проблемой и исправил ее, сохранив всплывающий контроллер в сильной переменной экземпляра, как было предложено, И явно отклонив его перед сбросом свойства с новым всплывающим контроллером, выделенным во втором запуске действия. В вашем примере вы должны добавить что-то вроде этого:
- (IBAction)createNewScore:(id)sender {
if (self.pc) {
[self.pc dismissPopoverAnimated:YES];
}
Комментарии:
1. Для меня сработало просто создание переменной экземпляра. Прикосновение к внешней стороне всплывающего окна также отменяет его позже, если вы не хотите использовать кнопку.
Ответ №2:
Если ваше всплывающее окно хранится как надежная ссылка, его нельзя освободить. Единственная возможность, когда его можно освободить, — это ситуация, когда объект, содержащий строгую ссылку ( self
в вашем примере), также освобождается.
Я думаю, что важный вопрос заключается в том, что вы делаете со своими представлениями, когда всплывающее окно видно.
Если вы уже проверили это, то это должно быть ошибкой фреймворка.