Вызывается UIPopoverController dealloc—ARC environment

#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 в вашем примере), также освобождается.

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

Если вы уже проверили это, то это должно быть ошибкой фреймворка.