Подтвердить что ты не робот

Избегание EXC_BAD_ACCESS при использовании шаблона делегата

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) сохраните массив загрузчиков в контроллере представления и установите их значения делегатов на нуль, когда я освобожу контроллер представления.

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Сбой происходит потому, что делегат

  • Имеет атрибут assign, AND
  • Был освобожден.

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

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

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

Ответ 3

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

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

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

self.delegate != nil

Ответ 4

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

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

Ответ 5

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