Избегание EXC_BAD_ACCESS при использовании шаблона делегата - PullRequest
18 голосов
/ 21 апреля 2010

A имеет контроллер представления, и он создает объект «загрузчик», который имеет ссылку на контроллер представления (как делегат). Загрузчик вызывает обратно контроллер представления, если он успешно загружает элемент. Это прекрасно работает, пока вы остаетесь на виде, но если вы уйдете до завершения загрузки, я получу EXC_BAD_ACCESS. Я понимаю, почему это происходит, но есть ли способ проверить, выделен ли объект по-прежнему? Я пытался протестировать, используя delegate != nil и [delegate respondsToSelector:], но он задыхается.

if (!self.delegate || ![self.delegate respondsToSelector:@selector(downloadComplete:)]) {
  // delegate is gone, go away quietly
        [self autorelease];
        return;
    }
else {
  // delegate is still around
  [self.delegate downloadComplete:result];
}

Я знаю, что мог,

a) чтобы объекты загрузчика сохраняли контроллер представления

b) сохранить массив загрузчиков в контроллере представления и установить их значения делегатов равными nil, когда я освобождаю контроллер представления.

Но мне интересно, есть ли более простой способ, где я просто проверяю, содержит ли адрес делегата допустимый объект?

Ответы [ 5 ]

27 голосов
/ 07 июня 2014

Я просто столкнулся с этой проблемой и решил ее. Для ARC решение состоит в том, чтобы использовать атрибут weak вместо assign.

Авария произошла потому, что делегат

  1. Имеет атрибут assign И
  2. был освобожден.

Решением является использование атрибута weak, потому что когда объект освобождается, указатель WILL будет установлен на nil. Поэтому, когда ваш код вызывает respondsToSelector на nil, Objective C будет игнорировать вызов, а не аварийно завершать работу.

В вашем коде, когда вы пытаетесь вызвать метод respondsToSelector для delegate, вы получаете EXC_BAD_ACCESS. Это связано с тем, что объекты, которые используют свойство assign, не будут установлены в nil, когда они освобождены. (Следовательно, почему выполнение !self.delegate перед respondsToSelector не препятствует вызову responseToSelector для освобожденного объекта и все же приводит к сбою вашего кода)

Как уже упоминалось, использование атрибута strong или assign для делегата (как уже упоминалось) в ARC приведет к циклу сохранения. Так что не делай этого, тебе не нужно.

10 голосов
/ 21 апреля 2010

Нет, вы не можете (с пользой) проверить, содержит ли адрес допустимый объект. Даже если бы вам удалось поработать внутри системы распределения памяти и определить, что ваш адрес указывает на допустимый объект, это не обязательно означает, что это был тот же объект, на который вы ранее ссылались : объект мог быть освобожден, а другой объект был создан по тому же адресу памяти.

Задержание делегата является обычным способом решения этой проблемы. Ваш вариант (b) нарушает инкапсуляцию объектов и может иметь проблемы с безопасностью потоков.

1 голос
/ 24 июля 2013

У меня также есть проблемы со слабой ссылкой делегата, и в настоящее время у меня есть только одно решение этой проблемы: используйте сильную ссылку для делегата и вручную установите self.delegate = nil; после того, как мой код завершен. Это решение работает для меня при загрузке асинхронных изображений, где у вас есть некоторый жизненный цикл с видимым окончанием.

1 голос
/ 25 октября 2011

Я сталкивался с этим вопросом, потому что мой объект "загрузчик" давал мне EXC_BAD_ACCESS. Мое решение состояло в том, чтобы отменить объект загрузчика прямо перед тем, как я выпустил его. Предполагая, что вы используете NSURLConnection в своем объекте загрузчика, вызовите для него метод cancel.

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

1 голос
/ 22 апреля 2010

Я бы просто написал

SEL slc = @selector(theSlc);
if ([delegate respondsToSelector:slc]) {
    [delegate performSelector:slc];
}

Если объект действителен, метод будет вызван, в противном случае нет. Вам не нужно проверять

self.delegate != nil
...