Сбой UIWebView EXC_BAD_ACCESS

#iphone #uiwebview #exc-bad-access

#iPhone #uiwebview #исключение-bad-access

Вопрос:

У меня сбой приложения, которое использует UIWebView. Обычно это происходит, когда страница загружена не полностью, и UIWebView отправляет селектор остановки загрузки. Или когда UIWebView полностью загрузил страницу. У меня есть EXC_BAD_ACCESS . Стек выглядит следующим образом:

 #0  0x95bb7688 in objc_msgSend
#1  0x30a671db in -[UIWebView webView:decidePolicyForNavigationAction:request:frame:decisionListener:]
#2  0x3024a10d in __invoking___
#3  0x30249ff8 in -[NSInvocation invoke]
#4  0x358ab160 in HandleDelegateSource
#5  0x302452c1 in CFRunLoopRunSpecific
#6  0x30244628 in CFRunLoopRunInMode
#7  0x32044c31 in GSEventRunModal
#8  0x32044cf6 in GSEventRun
#9  0x309021ee in UIApplicationMain
#10 0x0000239c in main at main.m:13
  

для меня самым странным здесь является webView:decidePolicyForNavigationAction:request:frame:decisionListener: селектор, отправленный в UIWebView, потому что в документации UIWebView такого селектора нет! Только для Cocoa (не для cocoa touch) WebView.
Я подозреваю, что что-то не так с UIWebView или его делегатом. Но я не могу установить точку останова для их просмотра. Пожалуйста, посоветуйте, как я могу получить больше информации в этой ситуации.

Комментарии:

1. Как насчет [UIWebView WebView:decidePolicyForNavigationAction:request:frame:decisionListener:]??

Ответ №1:

Вы должны прекратить загрузку WebView и удалить делегата перед выходом из представления:

 // ARC (correct solution)
- (void)dealloc {
    [_webView setDelegate:nil];
    [_webView stopLoading];
}

// non ARC
- (void)dealloc {
    [webView setDelegate:nil];
    [webView stopLoading];
    [webView release];
    [super dealloc];
}

// ARC (older solution)
- (void)viewWillUnload {
    [webView setDelegate:nil];
    [webView stopLoading];
}
  

Что говорится в документации Apple:
Важно перед выпуском экземпляра UIWebView, для которого вы установили делегат, вы должны сначала установить его свойству delegate значение nil. Это можно сделать, например, в вашем методе dealloc.

Комментарии:

1. В ios 6 используйте метод dealloc с ARC (без вызова super или какого-либо кода освобождения памяти) — viewWillUnload устарел в ios 6.

2. Это не работает в iOS 6, как указал @MortenHolmgaard. Смотрите мой ответ ниже.

3. Я не понимаю, почему dealloc — это версия, отличная от arc. В ARC вы не можете вызвать dealloc вручную, но он вызывается системой автоматически, и код в dealloc выполняется.

4. Использование dealloc должно быть стандартным методом для arc и не-arc. в версии arc опустите [WebView release]; и [super dealloc];. При использовании приведенного выше решения, если вы представите полноэкранный режим, будет вызван режим -viewWillDisappear:, и вам придется перепрограммировать ваш webview при вызове режима -viewWillAppear:.

5. использование [_webView setDelegate:nil]; спасите мой день! У меня есть viecontrolle, который вызывает другой, использующий webview, и произошел сбой. Теперь это работает!

Ответ №2:

Попробуйте включить NSZombie и посмотрите, не выходит ли что-то слишком рано.

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

В этом случае обратная трассировка выглядит так, как будто это делегат, который выпускается досрочно. Отношения делегирования, как правило, слабые и из-за отсутствия GC являются прекрасным источником оборванных ссылок, которые вызывают сбои, похожие на эти.

Ответ №3:

Просмотр исчезнет — это рабочий вариант принятого ответа:

 // ARC
- (void)viewWillDisappear:(BOOL)animated{
    [self.webView setDelegate:nil];
    [self.webView stopLoading];
}
  

Это отлично работает для iOS 6.

Ответ №4:

У меня был сбой EXC_BAD_ACCESS при прокрутке UIWebView, но только на iPad и только тогда, когда пользователь оставил прокрутку UIWebView, когда он / она закрыл содержащий его контроллер просмотра.

Установка для делегата значения nil не решила мою проблему здесь, но я нашел решение в другом месте по другой проблеме. Я добавил этот код в метод, вызываемый моей кнопкой закрытия:

 for (id subview in webView.subviews){
        if ([[subview class] isSubclassOfClass: [UIScrollView class]]){
            [subview setContentOffset:CGPointZero animated:NO];
        }
    }
  

Это останавливает прокрутку перед вызовом метода dealloc, в чем, по-видимому, и заключается проблема.

Ответ №5:

Я также видел эту точную ошибку, и она была вызвана тем, что делегат, который я назначил для UIWebView, не был сохранен (в моем случае UIViewController).

Ответ №6:

Обработка отмены регистрации делегата из webview и остановка загрузки webview лучше всего обрабатываются методом dealloc ViewController.

В качестве примера, когда viewWillDisappear может завершиться с ошибкой: если ViewController является дочерним по отношению к другому ViewController, вы можете запустить удаление ViewController view из родительского ViewController view с анимацией. В то же время вы можете удалить ViewController из его родительского элемента и обнулить его ссылку. В этот момент ViewController будет равен нулю, и viewWillDisappear никогда не будет вызван, что означает, что делегат WebView никогда не будет очищен.

Используйте dealloc и убедитесь, что ваш WebView всегда очищается.