MKMapView по-прежнему отправляет сообщения делегату после того, как супервизор был отключен
EDIT: изменено название. Я не знал этого в то время, но это дубликат Почему я сбой после освобождения MKMapView, если я больше не использую его?
Этот вопрос похож на Почему объект не отменяется при использовании ARC + NSZombieEnabled, но достаточно разный, что я думал, что стоит выбросить его на случай, если кто-нибудь поймет и может объяснить мне, что происходит. Другой вопрос может быть ошибкой XCode, поэтому я предполагаю, что это может быть похоже.
Сценарий:
-
RootViewController
имеет tableView
, отображающий кучу элементов
- Выбор ячейки представляет модальный
detailViewController
, содержащий другой tableView
- Одна из ячеек таблицы в
detailViewController
содержит MKMapView
, показывающую расположение элемента
-
mapView.delegate = detailViewController
- Отключить модальный
detailViewController
Вскоре после этого приложение сбой b/c MKMapView
отправляет mapView:viewForAnnotation:
в dealloc'ed detailViewController
. Этот сбой, воспроизведенный на пользовательском устройстве с раскладкой ad-hoc, поэтому проблема не имеет ничего общего с NSZombieEnabled
.
Мне удалось решить проблему, добавив:
_mapView.delegate = nil;
к dealloc
методу tableViewCell
, содержащему mapView.
ВОПРОС: почему необходимо, чтобы делегат был отключен, когда ячейка отменена? Похоже, что mapView
должен быть отменен ARC, когда ячейка освобождена, оставив это ненужным. Это хорошая практика для ноль делегатов, но я не думал, что это потребуется в этом случае.
EDIT: все подпункты как detailViewController
, так и UITableViewCells
объявлены как (nonatomic, strong)
свойства ala:
@property (nonatomic, strong) MKMapView * mapView;
РЕДАКТИРОВАТЬ 2: Думаю, мне нужно лучше читать документы. @fluchtpunkt верен. Здесь соответствующая информация из документации MKMapView
:
Перед выпуском объекта MKMapView, для которого вы установили делегат, не забудьте установить для свойства делегирования объектов значение nil. Один место, которое вы можете сделать, это метод dealloc, в котором вы распоряжаетесь вид карты.
Ответы
Ответ 1
MKMapView не скомпилирован с ARC, и из-за этого свойство для delegate
по-прежнему объявляется как assign
вместо weak
.
Из Документация MKMapView:
@property(nonatomic, assign) id<MKMapViewDelegate> delegate
И из Переход к заметкам о выпуске ARC:
Вы можете реализовать метод dealloc, если вам нужно управлять ресурсами, отличными от освобождения переменных экземпляра. Вам не нужно (действительно, вы не можете) выпускать переменные экземпляра, но вам может потребоваться вызвать [systemClassInstance setDelegate: nil] в системных классах и другой код, который не скомпилирован с использованием ARC.
Для делегатов системных классов (NS *, UI *) вам необходимо использовать "старое" правило установки делегатов на нуль при освобождении объекта делегирования.
поэтому добавьте метод dealloc к вашему detailViewController
- (void)dealloc {
self.mapView.delegate = nil;
}
Ответ 2
Хотя верно, что делегаты для таких классов должны быть явно установлены на nil
, сделать это в dealloc
уже слишком поздно. Вы уже потеряли ссылку на mapview во время viewDidUnload
. Вы должны сделать self.mapView.delegate = nil
ПЕРЕД viewDidUnload
(возможно, viewWillDisappear
или viewDidDisappear
)
По моему опыту, ведут себя только MKMapView
и UIWebView
.