MKMapView по-прежнему отправляет сообщения делегату после того, как его суперпредставление было отменено - PullRequest
14 голосов
/ 20 декабря 2011

РЕДАКТИРОВАТЬ: изменил название. Я не знал этого в то время, но это дубликат Почему я вылетал после освобождения MKMapView, если я больше не использую его?

<Ч />

Этот вопрос похож на Почему объект не освобождается при использовании ARC + NSZombieEnabled , но настолько отличается, что я подумал, что его стоит выбросить, если кто-то поймет и сможет объяснить мне, что происходит. Другим вопросом может быть ошибка XCode, поэтому я предполагаю, что это может быть похоже.

Сценарий:

  1. RootViewController имеет tableView, отображающий кучу предметов
  2. Выбор ячейки представляет модальное значение detailViewController, содержащее еще один tableView
  3. Одна из ячеек таблицы в detailViewController содержит MKMapView, показывающий местоположение элемента
  4. mapView.delegate = detailViewController
  5. Отклонить модал detailViewController

Вскоре после этого происходит сбой приложения из-за того, что MKMapView отправляет mapView:viewForAnnotation: в уже освобожденный detailViewController. Этот сбой воспроизводился на пользовательском устройстве со специальной сборкой дистрибутива, поэтому проблема не имеет ничего общего с NSZombieEnabled.

Мне удалось устранить ошибку, добавив:

_mapView.delegate = nil;

к dealloc методу tableViewCell, содержащему mapView.

ВОПРОС: почему необходимо обнулить делегата, когда ячейка освобождается? Кажется, что ARC должен освобождать mapView, когда ячейка освобождается, оставляя это ненужным. Это хорошая практика, чтобы ноль делегатов, но я не думал, что это потребуется в этом случае.

РЕДАКТИРОВАТЬ: все подпредставления как detailViewController, так и UITableViewCells объявлены как (nonatomic, strong) свойства ala:

@property (nonatomic, strong)   MKMapView *         mapView;

РЕДАКТИРОВАТЬ 2: Думаю, мне нужно лучше читать документы. @fluchtpunkt правильно. Вот соответствующая информация из MKMapView документации:

Перед выпуском объекта MKMapView, для которого вы установили делегат, не забудьте установить свойство делегата этого объекта на ноль. Один место, где вы можете сделать это в методе dealloc, где вы избавляетесь от вид карты.

Ответы [ 2 ]

19 голосов
/ 20 декабря 2011

MKMapView не скомпилирован с ARC, и поэтому свойство для delegate по-прежнему объявлено как assign вместо weak.
Из документации MKMapView :

@property(nonatomic, assign) id<MKMapViewDelegate> delegate

А из Переход к заметкам о выпуске ARC :

Вы можете реализовать метод dealloc, если вам нужно управлять ресурсами, отличными от освобождения переменных экземпляра. Вам не нужно (на самом деле вы не можете) освобождать переменные экземпляра, но вам может потребоваться вызвать [systemClassInstance setDelegate: nil] для системных классов и другого кода, который не скомпилирован с использованием ARC.


Для делегатов системных классов (NS *, UI *) вы должны использовать «старое» правило установки делегатов равным nil при освобождении объекта делегата.

поэтому добавьте метод dealloc к вашему detailViewController

- (void)dealloc {
    self.mapView.delegate = nil;
}
0 голосов
/ 20 декабря 2011

Хотя верно, что делегаты для таких классов должны быть явно установлены на nil, делать это в dealloc уже слишком поздно.Вы уже потеряли свою ссылку на карту во время viewDidUnload.Вы должны сделать self.mapView.delegate = nil ДО viewDidUnload (так, вероятно, viewWillDisappear или viewDidDisappear)

Из моего опыта, только MKMapView и UIWebView ведут себя таким образом.

...